简体   繁体   English

在Scala中正确实现2类参数Functor

[英]Proper implementation for a 2 type parameters Functor in Scala

I saw this question several times on SO, but no matter how hard I try, I can't make the following code compile. 我在SO上多次看到这个问题,但无论我怎么努力,我都无法编译以下代码。 The goal is to implement an Functor implementation for a simpler Reader (the code is here ): 目标是为更简单的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

I tried to bypass the implicit mechanism with the following: 我尝试用以下方法绕过隐式机制:

FunctorOps(foo).fmap(_ + 1)

but this outputs the following compilation error: 但是这会输出以下编译错误:

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)

Thank you in advance for your help. 预先感谢您的帮助。

UPDATE UPDATE

Just to make sure my FunctorOps is right, I created a functor instance for Id : 为了确保我的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

So the problem does not come from the FunctorOps implicit class. 所以问题不是来自FunctorOps隐式类。 I suspect Scala to have a real hard time with type lambdas... 我怀疑斯卡拉在类型lambdas上真的很难过......

UPDATE 2 更新2

I tried to simplify the problem but without success: 我试图简化问题,但没有成功:

  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)

UPDATE: 更新:
I think that, to have this working, you need to enable some extra options for the compiler in build.sbt : 我认为,要使其工作,您需要在build.sbt为编译器启用一些额外的选项:

scalacOptions ++= Seq(
      "-Ypartial-unification",
      "-language:postfixOps",
      "-language:higherKinds",
      "-deprecation",
      "-encoding", "UTF-8",
      "-feature",      
      "-unchecked"
    )

More info on the partial unification flag and what it solves can be found here . 有关部分统一标志及其解决方法的更多信息,请点击此处

ORIGINAL ANSWER : Are you running your code through Worksheet or a Scratch in IDEA? 原始答案 :您是通过工作表还是IDEA中的Scratch运行代码? I have noticed that sometimes, especially in these kind of functional programming tasks where there is type inference, implicit resolution and higher kinded type "magic", IDEA's REPLs are not up to the task (but I am not sure why). 我注意到有时候,特别是在这类函数式编程任务中,存在类型推断,隐式解析和更高级的“魔术”类型,IDEA的REPL不能完成任务(但我不确定为什么)。

This said, I tried to run the following on IDEA: 这说,我试图在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"))
}

And it works fine, printing 5 and 12 . 它工作正常,打印512 Also, as someone else mentioned, your code works on Scastie, which is another syntom of IDEA's acting up. 此外,正如其他人提到的,你的代码适用于Scastie,这是IDEA表演的另一个合成。

One final note: you probably already know this, but you can avoid all that type-lambda ugliness using the kind-projector compiler plugin . 最后要注意的是:你可能已经知道了这一点,但你可以使用kind-projector编译器插件来避免所有类型 - lambda ugliness。

Long story short, drop the ReaderF[X] type alias, and make your functor instance look like this: 简而言之,删除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)))
    }

Which is more readable IMHO. 哪个更具可读性恕我直言。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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