Scala GADT实例Functor

时间:2016-02-08 22:11:17

标签: scala inheritance scalaz gadt scala-cats

在哈斯克尔,我得到了:

data Foo a where
  Bar :: a -> Foo a
  Map :: (a -> b) -> Foo a -> Foo b

instance Functor Foo where
  fmap = Map

在Scala中我提出了:

import cats.Functor

trait Foo[A]
case class Bar[A](t: A) extends Foo[A]
case class Map[A,B](fun: A => B,foo: Foo[A]) extends Foo[B]

implicit val fooFunctor: Functor[Foo] = new Functor[Foo] {
  def map[A,B](fa: Foo[A])(f: A => B) = Map(f,fa)
}

但是Bar(40).map(_+2)给了我:

error: value map is not a member of Bar[Int]

我对Scala很新,并且不太了解继承方式。

我缺少什么?

2 个答案:

答案 0 :(得分:4)

您需要明确地将Bar(40)转发给Foo,并导入语法含义:

import cats.syntax.functor._
(Bar(40): Foo[Int]).map(_+2)

您需要upcast,因为Scala会推断表达式Bar[Int]的类型Bar(40),然后这会干扰查找添加map方法的相应隐式。因此,您有时会在配套对象中看到辅助方法为您执行upcast:

object Foo {
  def bar[A](a: A): Foo[A] = Bar(a)
  // etc.
}

答案 1 :(得分:0)

我想了解一下如何解决这个问题。基本上,即使只有超类型是仿函数,我们也希望提供仿函数操作。似乎无法表达,一些较高级的类型是另一种类型的超类型,例如这样的事情是不可能的:def bla[F[_], R[_] <: F]

然而,我们可以与B的仿函数一起隐式地提供从R []到F []的转换:

abstract class ProvidesFor[R,F[_],FF[_[_]],A](val value: FF[F]) {
  def convert(r: R): F[A]
}
implicit def providesFunctorForBar[A] =
  new ProvidesFor[Bar[A],Foo,Functor,A](Functor[Foo]) {
    override def convert(r: Bar[A]): Foo[A] = r
  }

因此,隐含的val将为我们提供Foo的仿函数以及从Bar到Foo的转换。

现在我们可以像这样向Functor.Ops提供隐式转换:

implicit class FunctorOps[R[_],F[_],A](target: R[A])(implicit tc: ProvidesFor[R[A],F,Functor,A])
      extends Functor.Ops[F,A] {
  override val self = tc.convert(target)
  override val typeClassInstance = tc.value
  override def map[B](f: A => B): F[B] = typeClassInstance.map(self)(f)
  override def imap[B](f: A => B)(g: B => A): F[B] = map(f)
}

现在这可以按预期工作:

Bar(1).imap(_+2)(_+5)

我们也可以对Map执行相同的操作:

implicit def providesFunctorForMap[A,B] =
  new ProvidesFor[Map[A,B],Foo,Functor,B](Functor[Foo]) {
    override def convert(r: Map[A,B]): Foo[B] = r
  }

Map((_:Int) + 1,Bar(5)).map(_+2)

由于某些奇怪的原因,我必须在扩展Functor.Ops时实现map和imap,即使这些方法不是抽象的。事实上,当我没有实现它们时,它编译得很好,但它在运行时因AbstractMethodError而失败。所以编译器认为实现是存在的,但事实并非如此。我怀疑他们正在使用某种字节码优化工具来删除这些实现。