I have the following functions:
f: a -> m[b]
g: (b,c) -> m[d]
h: (a,c) -> m[d]
How can h be expressed as a composition of f and g?
Using do/for notation we can implement h easily like so:
h: (a,c) => {
for {
b <- f(a)
d <- g(b,c)
} yield (d)
}
However, I'm curious if we can express it like so: h = f andThen g where andThen is used like a monadic composition operator. For example:
f: a -> m[b]
g: b -> m[c]
h: a -> m[c] = f andThen g
I'm assuming that creating such an andThen function is possible in languages like Haskell (e.g., Kliesli >=>). In Scala we can write one like so: (example in Scala naming andThenE since andThen is already defined on instance of Function1).
implicit class AndThenEither[A,B](val e: Function1[A,Either[_,B]]) {
def andThenE[C](f:Function1[B, Either[_,C]]): Function1[A, Either[_,C]] = {
(v1: A) => e.apply(v1).flatMap(b => f.apply(b))
}
}
Given this, it seems if we curry the functions we may be able to achieve such a composition (or at least it looks possible):
f: a -> m[b]
g: b -> c -> m[d]
h: a -> c -> m[d] = f andThen g
In theory this could work but I have no idea if this is possible or how to go about implementing something like this in Scala (or Haskell, although I'm more fluent with the former).
Let's say we had the following functions:
case class Error(e:String)
case class Output(i: Int, f: Float, s: String)
case class IntermediateOutput(i:Int, f:Float)
def f(i:Int): Either[Error, IntermediateOutput] = Right(IntermediateOutput(i+1, i*0.33)
def g(io: IntermediateOutput, s: String): Either[Error, Output] = Right(Output(io.i, io.f, "hello "+s))
val h: (Int, String) => Either[Error, Output] = f andThen g
val result = h(1, "world!") //Right(Output(2, 0.33, "hello world!")
Is this even possible/achievable? If not Scala, how could we go about curry composing monadic functions in Haskell or in general?
Is this a known thing or do we explicitly distinguish between currying being applicable to non-monadic functions and reserving the andThen like operator for monadic ones, but avoid mixing the two? If so, I can see a strong case for the do/for notation. However, I'm not entirely convinced that it's impossible and would like to understand this further. Perhaps the code would just be cluttered and that's okay - I'm simply curious. I stumbled on such a situation as a result of working on an existing problem and I couldn't cast it like so.