如何在Scala中为实例(上对象)方法定义HKT?

时间:2019-03-28 21:47:07

标签: scala monads traits higher-kinded-types

如何在Scala中定义一个HKT,以提供诸如map之类的方法作为实例上的方法,而不是对象上的函数?

我知道你可以

trait M[F[_]] {
  def map[A, B](f: F[A])(fn: A => B): F[B]
}

object Q extends M[Q] {
  def map[A, B](f: Q[A])(fn: A => B) = new Q(fn(f.e))
}

class Q[A](val e: A)

Q.map(new Q(1))(_.toDouble)

但是,我想在实例上使用map。我还没有看过任何有关此事的文献,所以我写了以下文章。

trait M[A, F[A]] {
  def map[B](f: A => B): F[B]
}

class Q[A](val e: A) extends M[A, Q] {
  def map[B](f: A => B) = new Q(f(e))
}

new Q(1).map(_.toDouble)

是惯用的Scala吗?它会执行map应该做什么?与上述对象版本相比,是否有限制? (顺便说一句,extends M[A, Q[A]]无法编译-为什么?)

我见过

trait M[A] {
  def map[B](f: A => B): M[B]
}

但随后Q的{​​{1}}可能会返回不需要的map

1 个答案:

答案 0 :(得分:4)

有一个专门用于此的库:https://github.com/mpilquist/simulacrum

您也可以自己做。典型的模式是定义一个相应的隐式MOps类或其他任何东西:

trait M[F[_]] {
  def map[A, B](f: F[A])(fn: A => B): F[B]
}

implicit class MOps[F[_] : M, A](x: F[A]) {
  def map[B](f: A => B): F[B] = implicitly[M[F]].map(x)(f)
}

然后是示例用法:

case class Box[A](a: A)

implicit val BoxM = new M[Box] {
  def map[A, B](f: Box[A])(fn: A => B): Box[B] = Box(fn(f.a))
}

Box(2).map(_.toString)

但是它有很多样板,所以Simulacrum可以为您(甚至更多)做这一切。