[英]Proper implementation for a 2 type parameters Functor in Scala
我在SO上多次看到這個問題,但無論我怎么努力,我都無法編譯以下代碼。 目標是為更簡單的Reader
實現Functor
實現(代碼在這里 ):
trait Functor[F[_]] {
def fmap[A, B](fa: F[A])(f: A => B): F[B]
}
implicit class FunctorOps[F[_]: Functor, A](self: F[A]) {
def fmap[B](f: A => B): F[B] = implicitly[Functor[F]].fmap(self)(f)
}
case class Reader[A, B](run: A => B)
type ReaderF[X] = ({ type L[A] = Reader[X, A] })
implicit def readerFunctors[E]: Functor[ReaderF[E]#L] =
new Functor[ReaderF[E]#L] {
override def fmap[A, B](fa: Reader[E, A])(f: A => B): Reader[E, B] =
Reader(e => f(fa.run(e)))
}
val foo = Reader[String, Int](_ => 42)
foo.fmap(_ + 1) // does not compile
我嘗試用以下方法繞過隱式機制:
FunctorOps(foo).fmap(_ + 1)
但是這會輸出以下編譯錯誤:
Error:(82, 23) type mismatch;
found : com.fp.Scratchpad.Reader[String,Int]
required: ?F[?A]
Note that implicit conversions are not applicable because they are ambiguous:
both method ArrowAssoc in object Predef of type [A](self: A)ArrowAssoc[A]
and method Ensuring in object Predef of type [A](self: A)Ensuring[A]
are possible conversion functions from com.fp.Scratchpad.Reader[String,Int] to ?F[?A]
FunctorOps(foo).fmap(_ + 1)
預先感謝您的幫助。
UPDATE
為了確保我的FunctorOps是正確的,我為Id
創建了一個functor實例:
case class Id[A](value: A)
implicit val idF: Functor[Id] = new Functor[Id] {
override def fmap[A, B](fa: Id[A])(f: A => B): Id[B] = Id(f(fa.value))
}
val id = Id(42)
id.fmap(_ + 1) // compiles
所以問題不是來自FunctorOps
隱式類。 我懷疑斯卡拉在類型lambdas上真的很難過......
更新2
我試圖簡化問題,但沒有成功:
trait Functor[F[_]] {
def map[A, B](x: F[A])(f: A => B): F[B]
}
implicit class Ops[F[_], A](fa: F[A])(implicit F: Functor[F]) {
def map[B](f: A => B): F[B] = F.map(fa)(f)
}
type FF[A] = ({ type F[B] = A => B })
implicit def ff[E]: Functor[FF[E]#F] = new Functor[FF[E]#F] {
override def map[A, B](x: E => A)(f: A => B): E => B = e => f(x(e))
}
val f: String => Int = _ => 42
val value: Functor[FF[String]#F] = ff[String]
val ops = new Ops[FF[String]#F, Int](f)(value)
// These compile
ops.map(_ + 1)("")
value.map(f)(_ + 1)("")
// This not
f.map(_ + 1)
更新:
我認為,要使其工作,您需要在build.sbt
為編譯器啟用一些額外的選項:
scalacOptions ++= Seq(
"-Ypartial-unification",
"-language:postfixOps",
"-language:higherKinds",
"-deprecation",
"-encoding", "UTF-8",
"-feature",
"-unchecked"
)
原始答案 :您是通過工作表還是IDEA中的Scratch運行代碼? 我注意到有時候,特別是在這類函數式編程任務中,存在類型推斷,隱式解析和更高級的“魔術”類型,IDEA的REPL不能完成任務(但我不確定為什么)。
這說,我試圖在IDEA上運行以下內容:
object TestApp extends App{
trait Functor[F[_]] {
def fmap[A, B](fa: F[A])(f: A => B): F[B]
}
implicit class FunctorOps[F[_]: Functor, A](self: F[A]) {
def fmap[B](f: A => B): F[B] = implicitly[Functor[F]].fmap(self)(f)
}
case class Reader[A, B](run: A => B)
type ReaderF[X] = ({ type L[A] = Reader[X, A] })
implicit def readerFunctors[E]: Functor[ReaderF[E]#L] =
new Functor[ReaderF[E]#L] {
override def fmap[A, B](fa: Reader[E, A])(f: A => B): Reader[E, B] =
Reader(e => f(fa.run(e)))
}
val foo: Reader[String, Int] = Reader[String, Int](s => s.length)
val i = foo.fmap(_ + 1)
println(i.run("Test"))
println(i.run("Hello World"))
}
它工作正常,打印5
和12
。 此外,正如其他人提到的,你的代碼適用於Scastie,這是IDEA表演的另一個合成。
最后要注意的是:你可能已經知道了這一點,但你可以使用kind-projector編譯器插件來避免所有類型 - lambda ugliness。
簡而言之,刪除ReaderF[X]
類型別名,並使您的functor實例看起來像這樣:
implicit def readerFunctors[X]: Functor[Reader[X,?]] =
new Functor[Reader[X,?]] {
override def fmap[B, C](fa: Reader[X,B])(f: B => C): Reader[X,C] =
Reader(e => f(fa.run(e)))
}
哪個更具可讀性恕我直言。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.