Как настроить монадный трансформер в скалазе - PullRequest
0 голосов
/ 26 февраля 2020

Я изучаю скалаз в эти дни. Когда я сравниваю скаляр с Haskell, я обнаруживаю, что трудно настроить мой собственный монадный преобразователь.

Код, который я могу сделать в Haskell, вот так:

newtype Box a = Box a deriving Show
-- Monad instance of Box, it's simple, I pass it..
newtype BoxT m a = BoxT {runBoxT :: m (Box a)}

instance (Monad m) => Monad (BoxT m) where
    return a = BoxT $ return $ Box a
    x >>= f = BoxT $ do
        (Box v) <- runBoxT x
        runBoxT $ f v

instance (Monad m) => Applicative (BoxT m) where
    pure = return
    (<*>) = ap

instance (Monad m) => Functor (BoxT m) where
    fmap = liftM

Как можно Я делаю то же самое в скалазе? Я пробовал много способов, но, похоже, я не могу понять тип scala generi c, например:

case class Box[A](value:A)
case class BoxT[M[_],A](run: M[Box[A]])

implicit val monad = Monad[BoxT] { // goes wrong, type `BoxT[M[_],A]` doesn't match F[_]
 ... //don't know how to override functions..
}

1 Ответ

1 голос
/ 27 февраля 2020

Спасибо за комментарий @Iva Kam о добром проекторе.

Прежде всего, включил добрый проектор в build.sbt: https://github.com/typelevel/kind-projector

addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full)

Тогда код может работать так:

  case class Box[A](value:A)
  case class BoxT[M[_],A](run: M[Box[A]])

  implicit def monad[M[_]](implicit m : Monad[M]): Monad[BoxT[M, ?]] = new Monad[BoxT[M,?]] {
    override def point[A](a: => A): BoxT[M,A] = BoxT(m.point(Box(a)))

    override def bind[A, B](fa: BoxT[M,A])(f: A => BoxT[M,B]): BoxT[M,B] = BoxT(m.bind(fa.run){ b: Box[A] =>
      val v = b.value
      f(v).run
    })
  }

  def main(args: Array[String]): Unit = {
    val a = Monad[BoxT[Option,?]].point(2)
    val b = for {
      v <- a  //v should be a Int, but idea can't analyze it, maybe I should update, still in 2018.2
    } yield {v * 4}

    val c = a >> b //this line works, but idea can't analyze it :(
    println(b)
    println(c)
  }

Но похоже, что когда я использую монаду BoxT, я должен взять ? вместо A внутреннего типа Box.

...