基于委托的类型编码有什么问题

时间:2018-05-25 21:32:19

标签: scala functional-programming typeclass

类型一致性是一个众所周知的问题。

例如,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。你能指点我一下吗?

1 个答案:

答案 0 :(得分:3)

通过使用继承来表示它,您可以免费推断 - Monad自动为Functor。使用委托代表它,你不会。

你必须定义大量的隐式转换来解决这个问题,这将是一件痛苦的事。但是(简单的)隐式转换不会在Scala中自动链接,因此对于它们,它仍然不会通过中间类型类FunctorMonad推断Applicative

P.S。在实践中,如果你做得对,你通常不会在决议上产生歧义。这意味着,(a)使用foo.some而不是Some(foo)等,以及(b)使用值类或类似的语义消除您希望使用不同实例的类型的歧义。如果所有其他方法都失败了,请明确传入实例(您无法在Haskell中执行此操作)。