假设我有一些类型类:
trait Greeter[A] {
def greet(a: A): String
}
我想编写一个返回存在此类型类实例的值的方法:
implicit val intGreeter = new Greeter[Int] {
def greet(n: Int): String = s"Hello, integer $n!"
}
implicit val doubleGreeter = new Greeter[Double] {
def greet(d: Double): String = s"Hello, double $d"
}
def foo(b: Boolean): <X> = {
if (b) 3 else 1.0
}
其中<X>
是某种类型签名。然后像这样使用它:
val g: <X> = foo(true)
println(implicitly[Greeter[<X>]].greet(g))
当然,这并不直接起作用。我可以做这样的事情:
trait Greetable {
def greet: String
}
def getGreetable[A : Greeter](a: A) = new Greetable {
def greet: String = implicitly[Greeter[A]].greet(a)
}
def foo(b: Boolean): Greetable = {
if (b) getGreetable(3) else getGreetable(1.0)
}
这工作得很好,但它似乎有点不方便,而且不是真正的可扩展性。我必须为每个类型类定义相应的特征。如果我想要一个包含两个类型类实例的返回类型怎么办?还是n?这也感觉像是一种非常OO的方法;FP世界中是否有解决此问题的内容?
只要您的方法确实需要根据某些条件返回 3 或 1.0,并且该部分无法更改,那么您肯定需要返回类型的 sum 类型。
这可以通过一个公共超类(这是您采用的方法(或通过不相交的并集(例如Either
、scalaz./
等(进行建模,在这种情况下,您将返回例如Int / Double
.如果可能出现许多潜在的类型,那么您将需要"其中之一"或(更方便的(无形HList,或者干脆坚持超类Greetable
。
我认为这种情况的笨拙不是源于解决方案,而是源于问题本身。基于某些内部逻辑返回两种或多种不同类型的方法并不是真正的FP本身,是吗?:)