问题
I'm using the Reader
monad in Scala as provided by the scalaz library. I'm familiar with this monad as defined in Haskell. The problem is that I cannot find the functions equivalent to return
, local
, and sequence
(among others).
Currently I use constructs that I do not like since I'm repeating myself or making my code a bit obscure.
Regarding return
, I'm currently using:
Reader{_ => someValue}
I'd rather just use a construct like unit(someValue)
, but I could not find anything on the internet. There are tutorials like this one that use the approach above, and which I consider not optimal.
Regarding local
I also have to do something similar: instead of typing something like: local f myReader
I have to unfold its definition:
Reader{env => myReader.run(f(env))
Finally, sequence is a bit closer to what I would expect (being a Haskell refugee doing Scala):
readers: List[Reader[Env, T]]
readerTs: Reader[Env, List[T]] = readers.sequenceU
My problem with this implementation is that, being relatively new to Scala, the type of sequenceU
final class TraverseOps[F[_],A] private[syntax](val self: F[A])(implicit val F: Traverse[F]) extends Ops[F[A]] {
//...
def sequenceU(implicit G: Unapply[Applicative, A]): G.M[F[G.A]]
appears like rather obscure, and seems like black magic. Ideally I would like to use a sequence
operations on Monads.
Is there a better translation of these constructs to Scala available on scalaz or similar library? I'm not married to any Functional library for Scala, so any solution using other libraries will do, although I'd rather have an answer using scalaz, since I already implemented my code using it.
回答1:
To make the things simpler, I fill in some types. Changing them to defs with generic types should still work.
Also I extracted the ReaderInt
type, to avoid confusion with type lambdas.
return / pure / point
Scala does not have automatic typeclass resolution, so you need to provide them implicitly. For Kleisli
(being a monad transformer for reader),
Kleisli[Id, ?, ?]
is enough
implicit val KA = scalaz.Kleisli.kleisliIdApplicative[Int]
type ReaderInt[A] = Kleisli[Id.Id, Int, A]
val alwaysHello = KA.point("hello")
or with imported syntax:
import scalaz.syntax.applicative._
val alwaysHello = "hello".point[ReaderInt]
So as a general rule, you
1) import the applicative intance, which usually located in scalaz.std.something.somethingInstance
2) import scalaz.syntax.something._
3) then you can write x.point[F]
, where F
is your applicative.
local
Not sure, that it answers your question, but Kleisli
has a local
method.
val f: String ⇒ Int = _.length
val alwaysEleven = alwaysHello local f
sequencing
The same way, you are free to choose to use syntax
for or to specify type classes explicitly.
import scalaz.std.list.listInstance
val initial: List[ReaderInt[String]] = ???
val sequenced: ReaderInt[List[String]] = Traverse[List].sequence[ReaderInt, String](initial)
import scalaz.syntax.traverse._
val z = x.sequence[ReaderInt, String]
I prefer not to use sequenceU
, which uses Unapply
typelcass to infer the G
type, because sometimes scala has troubles of figuring out the right one.
And I personally do not find it messy to put in some types myself.
It may worth to look into cats, though it does not have much yet.
来源:https://stackoverflow.com/questions/38524384/reader-monad-in-scala-return-local-and-sequence