简体   繁体   中英

How can I define an HKT for instance (over object) methods in Scala?

How do you define an HKT in Scala to provide methods, such as map , as methods on instances instead of functions on objects?

I know you can

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)

However, I want to use map on instances instead. I haven't seen any literature that does this, so I've written the following.

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)

Is it idiomatic Scala? Does it do what map should do? Are there limitations compared with the object version above? (An aside, with extends M[A, Q[A]] it won't compile - why?)

I have seen

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

but then Q 's map could return a class R[A] extends M[A] which isn't desired.

There's a library specifically for this: https://github.com/mpilquist/simulacrum

You can also do it yourself. The typical pattern is to define a corresponding implicit MOps class or whatever:

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)
}

Then example usage:

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)

But it's a lot of boilerplate, so simulacrum does it all for you (and more).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM