2016-02-01 19 views
6

Karmaşık bir tür hiyerarşisine sahibim, ancak bunu çözmek için iki temel özellik var: Convertable ve Conversion[A <: Convertable, B <: Convertable, ör. Mealy-automaton'u Moore-automaton'a dönüştürebilen bir Dönüşüm var. Her Conversion[A,B], convert(automaton: A) : B yöntemine sahiptir.Bilinmeyen türler için HList üzerine katlama

Şimdi temel olarak birbiri ardına yapılacak olan normal Dönüşümler Listesi olan akıllı Dönüşümler kavramını tanıtmak istiyorum. Bu nedenle, val path : HList parametresine sahip olan ve dönüşüm zincirini temsil eden bir Dönüşüm'ü genişleten ve convert yöntemini uygulayan bir AutoConversion özelliğini ekledim, böylece OtomatikBağlantılar'ın almak için gerçek Dönüşümlerin listesini sağlaması gerekir. Ben işte benim ilk denemede, sen path üzerinde fold ile bunu uygulamak düşünüyorum: örtülü hiçbir Klasör bulunabilir çünkü,

package de.uni_luebeck.isp.conversions 

import shapeless._ 
import shapeless.ops.hlist.LeftFolder 

trait AutoConversion[A <: Convertable, B <: Convertable] extends Conversion[A, B] { 
    val path: HList 

    object combiner extends Poly { 
     implicit def doSmth[C <: Convertable, D <: Convertable] = 
     use((conv : Conversion[C, D] , automaton : C) => conv.convert(automaton)) 

}

override def convert(startAutomaton: A): B = { 
    path.foldLeft(startAutomaton)(combiner) 
    } 
} 

Bu işe yaramaz, Bu yüzden, Compiler için bir yere daha fazla Tip bilgisi vermem gerektiğini tahmin ediyorum, ama nerede olduğunu bilmiyorum nerede

cevap

9

Daha fazla yazım bilgisine ihtiyaç duyduğunuzda haklısınız ve genel olarak HList ile bir değeriniz varsa Statik bir tip, muhtemelen yaklaşımınızı değiştirmeniz gerekecektir. HList ile yapabileceğiniz hiçbir şey, HList (buna değerlerini eklemenin yanı sıra) olduğu halde yapabileceğiniz hiç bir şey yoktur ve genellikle yalnızca HList'u tip kısıtlaması olarak yazabilirsiniz.

Sizin durumunuzda, tanımladığınız şey, bir tür hizalanmış dizidir. Bu yaklaşımla ilerlemeden önce, gerçekten ihtiyacınız olduğuna gerçekten emin olmanızı öneririm. İşlevler (ve Conversion gibi işlev benzeri türler) ile ilgili güzel şeylerden biri, oluşturdukları: A => B ve B => C öğeleriniz vardır ve bunları bir A => C içine eklersiniz ve sonsuza kadar B'u unutabilirsiniz. Genelde tam olarak istediğiniz gibi olan güzel ve temiz bir kara kutu elde edersiniz. Bununla birlikte, bazı durumlarda, işlev benzeri şeyleri, boru hattının parçalarını yansıtabileceğiniz şekilde oluşturabilmeniz yararlı olabilir. Bunun bu davalardan biri olduğunu kabul edeceğim, ama bunu kendiniz için doğrulamanız gerekir. Eğer değilse, şanslısınız çünkü gelecek olan karışıklıktır.

Bu tipleri varsayıyoruz:

import shapeless._ 

trait TypeAligned[L <: HList] extends DepFn1[L] { 
    type I <: Convertable 
    type O <: Convertable 
    type Out = Conversion[I, O] 
} 

L:

trait Convertable 

trait Conversion[A <: Convertable, B <: Convertable] { 
    def convert(a: A): B 
} 

Biz belirli HList olan tipleri sıraya bir veya daha dönüşüm oluşur tanıklar bir tür sınıf tanımlayabilirsiniz Boru hattı ile ilgili tüm tip bilgileri içerir ve I ve O uç noktalarının türleridir.

Sonra (bu iki companioned edilecek için yukarıdaki özelliği ile birlikte tanımlanması gerekir unutmayın) bu tip sınıf için örneklerini gerekir:

object TypeAligned { 
    type Aux[L <: HList, A <: Convertable, B <: Convertable] = TypeAligned[L] { 
    type I = A 
    type O = B 
    } 

    implicit def firstTypeAligned[ 
    A <: Convertable, 
    B <: Convertable 
    ]: TypeAligned.Aux[Conversion[A, B] :: HNil, A, B] = 
    new TypeAligned[Conversion[A, B] :: HNil] { 
     type I = A 
     type O = B 
     def apply(l: Conversion[A, B] :: HNil): Conversion[A, B] = l.head 
    } 

    implicit def composedTypeAligned[ 
    A <: Convertable, 
    B <: Convertable, 
    C <: Convertable, 
    T <: HList 
    ](implicit 
    tta: TypeAligned.Aux[T, B, C] 
): TypeAligned.Aux[Conversion[A, B] :: T, A, C] = 
    new TypeAligned[Conversion[A, B] :: T] { 
     type I = A 
     type O = C 
     def apply(l: Conversion[A, B] :: T): Conversion[A, C] = 
     new Conversion[A, C] { 
      def convert(a: A): C = tta(l.tail).convert(l.head.convert(a)) 
     } 
    } 
} 

Şimdi de AutoConversion bir sürümünü yazabilirsiniz boru hattı ile ilgili tip bilgilerin tümünü izler:

class AutoConversion[L <: HList, A <: Convertable, B <: Convertable](
    path: L 
)(implicit ta: TypeAligned.Aux[L, A, B]) extends Conversion[A, B] { 
    def convert(a: A): B = ta(path).convert(a) 
} 

Ve bu gibi kullanabilirsiniz:

case class AutoA(i: Int) extends Convertable 
case class AutoB(s: String) extends Convertable 
case class AutoC(c: Char) extends Convertable 

val ab: Conversion[AutoA, AutoB] = new Conversion[AutoA, AutoB] { 
    def convert(a: AutoA): AutoB = AutoB(a.i.toString) 
} 

val bc: Conversion[AutoB, AutoC] = new Conversion[AutoB, AutoC] { 
    def convert(b: AutoB): AutoC = AutoC(b.s.lift(3).getOrElse('-')) 
} 

val conv = new AutoConversion(ab :: bc :: HNil) 

Ve conv beklenen statik türüne sahip olacak (ve Conversion[AutoA, AutoC] uygulayın).