2015-11-28 13 views
7

Jak wiadomo, shapeless zawiera listę typu HList (Heterogenous), która może zawierać wiele typów.Składanie listy różnych typów przy użyciu Shapeless w Scala

Czy można złożyć HList? na przykład:

// ref - Composable application architecture with reasonably priced monad 
// code - https://github.com/stew/reasonably-priced/blob/master/src/main/scala/reasonable/App.scala 

import scalaz.{Coproduct, Free, Id, NaturalTransformation} 

def or[F[_], G[_], H[_]](f: F ~> H, g: G ~> H): ({type cp[α] = Coproduct[F,G,α]})#cp ~> H = 
    new NaturalTransformation[({type cp[α] = Coproduct[F,G,α]})#cp,H] { 
    def apply[A](fa: Coproduct[F,G,A]): H[A] = fa.run match { 
     case -\/(ff) ⇒ f(ff) 
     case \/-(gg) ⇒ g(gg) 
    } 
    } 

type Language0[A] = Coproduct[InteractOp, AuthOp, A] 
type Language[A] = Coproduct[LogOp, Language0, A] 

val interpreter0: Language0 ~> Id = or(InteractInterpreter, AuthInterpreter) 
val interpreter: Language ~> Id = or(LogInterpreter, interpreter0) 


// What if we have `combine` function which folds HList 
val interpreters: Language ~> Id = combine(InteractInterpreter :: AuthInterpreter :: LoginInterpreter :: HNil) 

Czy mogę uprościć generowanie Langauge?

type Language0[A] = Coproduct[InteractOp, AuthOp, A] 
type Language[A] = Coproduct[LogOp, Language0, A] 

// What if we can create `Language` in one line 
type Language[A] = GenCoproduct[InteractOp, AuthOp, LogOp, A] 
+2

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). –

Odpowiedz

7

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.

+0

Dziękuję za miły przykład pracy. – 1ambda

Powiązane problemy