编写两个函数来获取返回HList的函数

时间:2015-07-23 12:53:40

标签: scala shapeless

这可能是关于shapeless的一个非常天真的问题:

假设我有函数A => M[B]B => M[C]。如何撰写它们以获得新功能A => M[B::C::HNil]

3 个答案:

答案 0 :(得分:5)

如果你想一般性地这样做,你可以使用Scalaz的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)

或者如果你想要一个HList而不是一个元组:

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)

现在您将函数包装在Kleisli箭头中:

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)

然后:

scala> println(c.run(10))
Some(aaaaaaaaaa :: 10 :: HNil)

通过将箭头限制在M上方的Kleisli箭头,您可以使类型推断(但也不那么通用)变得不那么繁琐。

答案 1 :(得分:2)

您需要定义M[_]Monad的某种形式,因此您可以flatMap,我选择使用scalaz Monad:< / p>

  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))
  }

答案 2 :(得分:1)

这应该可以解决问题

for {
  b <- f(a)
  c <- g(b)
} yield b :: c :: HNil

当然扩展到

f(a) flatMap { b =>
  g(b) map { c => b :: c :: HNil }
}