Dla kompletnego przykładu wykonania, załóżmy, że mamy kilka prostych algebr:
sealed trait AuthOp[A]
case class Login(user: String, pass: String) extends AuthOp[Option[String]]
case class HasPermission(user: String, access: String) extends AuthOp[Boolean]
sealed trait InteractOp[A]
case class Ask(prompt: String) extends InteractOp[String]
case class Tell(msg: String) extends InteractOp[Unit]
sealed trait LogOp[A]
case class Record(msg: String) extends LogOp[Unit]
a niektóre (bezsensowne ale kompilacji stanie) tłumaczy:
import scalaz.~>, scalaz.Id.Id
val AuthInterpreter: AuthOp ~> Id = new (AuthOp ~> Id) {
def apply[A](op: AuthOp[A]): A = op match {
case Login("foo", "bar") => Some("foo")
case Login(_, _) => None
case HasPermission("foo", "any") => true
case HasPermission(_, _) => false
}
}
val InteractInterpreter: InteractOp ~> Id = new (InteractOp ~> Id) {
def apply[A](op: InteractOp[A]): A = op match {
case Ask(p) => p
case Tell(_) =>()
}
}
val LogInterpreter: LogOp ~> Id = new (LogOp ~> Id) {
def apply[A](op: LogOp[A]): A = op match {
case Record(_) =>()
}
}
W tym momencie powinieneś być w stanie spasować tłumacza:
import scalaz.Coproduct
import shapeless.Poly2
object combine extends Poly2 {
implicit def or[F[_], G[_], H[_]]: Case.Aux[
F ~> H,
G ~> H,
({ type L[x] = Coproduct[F, G, x] })#L ~> H
] = at((f, g) =>
new (({ type L[x] = Coproduct[F, G, x] })#L ~> H) {
def apply[A](fa: Coproduct[F, G, A]): H[A] = fa.run.fold(f, g)
}
)
}
Ale to nie działa z powodów, które wydają się mieć coś wspólnego z wnioskiem typu. To nie jest zbyt trudne do napisania własnej klasy typu, choć:
import scalaz.Coproduct
import shapeless.{ DepFn1, HList, HNil, :: }
trait Interpreters[L <: HList] extends DepFn1[L]
object Interpreters {
type Aux[L <: HList, Out0] = Interpreters[L] { type Out = Out0 }
implicit def interpreters0[F[_], H[_]]: Aux[(F ~> H) :: HNil, F ~> H] =
new Interpreters[(F ~> H) :: HNil] {
type Out = F ~> H
def apply(in: (F ~> H) :: HNil): F ~> H = in.head
}
implicit def interpreters1[F[_], G[_], H[_], T <: HList](implicit
ti: Aux[T, G ~> H]
): Aux[(F ~> H) :: T, ({ type L[x] = Coproduct[F, G, x] })#L ~> H] =
new Interpreters[(F ~> H) :: T] {
type Out = ({ type L[x] = Coproduct[F, G, x] })#L ~> H
def apply(
in: (F ~> H) :: T
): ({ type L[x] = Coproduct[F, G, x] })#L ~> H =
new (({ type L[x] = Coproduct[F, G, x] })#L ~> H) {
def apply[A](fa: Coproduct[F, G, A]): H[A] =
fa.run.fold(in.head, ti(in.tail))
}
}
}
A potem można napisać combine
:
def combine[L <: HList](l: L)(implicit is: Interpreters[L]): is.Out = is(l)
i używać go:
type Language0[A] = Coproduct[InteractOp, AuthOp, A]
type Language[A] = Coproduct[LogOp, Language0, A]
val interpreter: Language ~> Id =
combine(LogInterpreter :: InteractInterpreter :: AuthInterpreter :: HNil)
Możesz być w stanie aby uzyskać wersję Poly2
, ale ta klasa prawdopodobnie byłaby dla mnie wystarczająco prosta. Niestety, nie będziesz w stanie uprościć definicji aliasu typu w sposób jaki chcesz.
Możesz spasować 'HList' używając funkcji polimorficznej. Proszę zapoznać się z [shapeless guide] (https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#heterogenous-lists). –