Stacking Monad Transformers in scala
Asked Answered
M

1

7

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?

Minstrelsy answered 21/7, 2016 at 16:10 Comment(5)
Minor point: In Haskell, I would not call this "stacking monad transformers" since I'd use that to refer to e.g. StateT s (ReaderT r IO) a. It is true, though, that in such stack we would have multiple type constraints, so it IS related.Inexorable
I don't think the two implicit parameters are the problem, otherwise you would get the error "ambiguous implicit values" (see github.com/scalaz/scalaz/issues/1110).Millburn
@Millburn if I pass only 1 implicit, the thing works :)Minstrelsy
Sorry, my comment was unfounded :) Interestingly it works when one parameter is explicit and the other one implicit; it doesn't work if both are either explicit or implicit.Millburn
Actually it works with 1 implicit and doesn't work with 1 explicit(e.g. justReader[F[_]](r: MonadReader[F, Int]) vs justReader[F[_]](implicit r: MonadReader[F, Int])), so I'm completely lost now.Minstrelsy
M
1

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
Millburn answered 22/7, 2016 at 13:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.