Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stacking Monad Transformers in scala

I'm trying to stack monad transfromers of scalaz in a haskell way:

statyReader :: (MonadReader Int m, MonadState Int m) => m Int

scala:

  def statyReader[F[_]](implicit r: MonadReader[F, Int], s: MonadState[F, Int]): F[Int] = for {
    counter <- s.get
    secret  <- r.ask
    _       <- s.put(counter + secret)
  } yield counter

It compiles with 1 implicit passed, but not with 2:

Error:(13, 18) value flatMap is not a member of type parameter F[Int]
    counter <- s.get
                 ^
Error:(14, 18) value flatMap is not a member of type parameter F[Int]
    secret  <- r.ask
                 ^
Error:(15, 21) value map is not a member of type parameter F[Unit]
    _       <- s.put(counter + secret)
                    ^

Why is this happening? My guess is that compiler is now confused which "monadic instance of F[_]" it should pick(both MonadReader and MonadState extend Monad[F[_]). Is this a right guess?

How to overcome this?

like image 375
Ilya Smagin Avatar asked Jul 21 '16 16:07

Ilya Smagin


1 Answers

This is not really an answer, but maybe helps a bit.

I think you're right; it seems like the compiler can't unify the F[_] types of both parameters (I guess since it is a higher-kinded type with multiple possible instances and not a concrete type instance) to a monad type. Compilation works with separate parameter lists since type unification only happens within a parameter list. It can be further illustrated like this:

def statyReader[F[_]](implicit r: MonadReader[F, Int], s: MonadState[F, Int]): F[Int] =
  statyReader2(r, s)

def statyReader2[F[_]:Monad](r: MonadReader[F, Int], s: MonadState[F, Int]): F[Int] =
  for {
    counter <- s.get
    secret <- r.ask
    _ <- s.put(counter + secret)
  } yield counter

Error: ambiguous implicit values: both
value s of type scalaz.MonadState[F,Int] and
value r of type scalaz.MonadReader[F,Int]
match expected type scalaz.Monad[F]

Obviously, as a workaround you could use two parameter lists to choose which monad you want to use:

def statyReader[F[_]](implicit r: MonadReader[F, Int], s: MonadState[F, Int]): F[Int] =
  statyReader2(r)(s)

def statyReader2[F[_]](r: MonadReader[F, Int])(implicit s: MonadState[F, Int]): F[Int] =
  for {
    counter <- s.get
    secret <- r.ask
    _ <- s.put(counter + secret)
  } yield counter
like image 108
devkat Avatar answered Sep 30 '22 18:09

devkat