Как я могу определить HKT для экземпляров (над объектом) методов в Scala? - PullRequest
3 голосов
/ 29 марта 2019

Как вы определяете HKT в Scala для предоставления методов, таких как 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 map может вернуть class R[A] extends M[A] что не желательно.

1 Ответ

4 голосов
/ 29 марта 2019

Специально для этого есть библиотека: 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 сделает все это за вас (и даже больше).

...