类型一致性是一个众所周知的问题。
例如,Monad
必须是Functor
,而Traversable
也必须是Functor
。如果通过继承(has to be
)表示Monad[F[_]] extends Functor[F[_]]
,则隐式解析中很可能存在歧义。
我应用了经验法则:如果你有继承问题,可以通过委托替换它。这是一个例子:
trait Semigroup[A] {
def append(a: A, b: A): A
}
trait Monoid[A] {
def zero(): A
val semigroup: Semigroup[A]
def append(a: A, b: A): A = semigroup.append(a, b)
}
implicit object IntSemigroup extends Semigroup[Int] {
override def append(a: Int, b: Int): Int = a + b
}
implicit object IntMonoid extends Monoid[Int] {
override def zero(): Int = 0
override val semigroup: Semigroup[Int] = IntSemigroup
}
def main(args: Array[String]): Unit = {
println(implicitly[Monoid[Int]].append(2, 3))
}
我猜这种方法不起作用,因为它很明显,但没有用于scalaz / cats。你能指点我一下吗?
答案 0 :(得分:3)
通过使用继承来表示它,您可以免费推断 - Monad
自动为Functor
。使用委托代表它,你不会。
你必须定义大量的隐式转换来解决这个问题,这将是一件痛苦的事。但是(简单的)隐式转换不会在Scala中自动链接,因此对于它们,它仍然不会通过中间类型类Functor
从Monad
推断Applicative
。
P.S。在实践中,如果你做得对,你通常不会在决议上产生歧义。这意味着,(a)使用foo.some
而不是Some(foo)
等,以及(b)使用值类或类似的语义消除您希望使用不同实例的类型的歧义。如果所有其他方法都失败了,请明确传入实例(您无法在Haskell中执行此操作)。