This is probably a very naive question about shapeless:
Suppose I have functions A => M[B]
and B => M[C]
. How can I compose them to get a new function A => M[B::C::HNil]
?
If you want to do this generically you can use Scalaz's Arrow
:
import scalaz._, Scalaz._
def andThenButKeep[Arr[_, _]: Arrow, A, B, C](
f: Arr[A, B],
g: Arr[B, C]
): Arr[A, (B, C)] = f >>> (Category[Arr].id &&& g)
Or if you want an HList
instead of a tuple:
import scalaz._, Scalaz._
import shapeless._, shapeless.syntax.std.tuple._
def andThenButKeep[Arr[_, _], A, B, C](
f: Arr[A, B],
g: Arr[B, C]
)(implicit Arr: Arrow[Arr]): Arr[A, B :: C :: HNil] =
f >>> (Arr.id &&& g) >>> Arr.arr((_: (B, C)).productElements)
Now you'd wrap your functions in a Kleisli arrow:
type OptionFunc[A, B] = Kleisli[Option, A, B]
val f: OptionFunc[Int, String] = Kleisli(i => Some("a" * i))
val g: OptionFunc[String, Int] = Kleisli(s => Some(s.length))
val c = andThenButKeep(f, g)
And then:
scala> println(c.run(10))
Some(aaaaaaaaaa :: 10 :: HNil)
You could make this a little less fussy about type inference (but also less generic) by restricting the arrow to a Kleisli arrow over your M
.
You need to define that M[_]
is some form of a Monad
so you can flatMap
it, I chose to use the scalaz
Monad
:
import scalaz._, Scalaz._
import shapeless._
def compose[M[_] : Monad, A, B, C](f: A => M[B], g: B => M[C]): A => M[B :: C :: HNil] = {
a => f(a).flatMap(b => g(b).map(c => b :: c :: HNil))
}
This should do the trick
for {
b <- f(a)
c <- g(b)
} yield b :: c :: HNil
which of course expands to
f(a) flatMap { b =>
g(b) map { c => b :: c :: HNil }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With