2016-02-01 17 views
6

私は複合型の階層を持っていますが、それを分解するために2つの基本的な特性があります:ConvertableConversion[A <: Convertable, B <: Convertableです。 Mealy-automatonをMoore-automatonに変換できる変換があります。 すべてConversion[A,B]にはconvert(automaton: A) : Bメソッドがあります。不明なタイプのHListを折りたたみます。

スマートコンバージョンのコンセプトを紹介します。これは基本的に通常のコンバージョンのリストであり、順番に実行されます。 したがって、AutoConversionの特性を導入し、val path : HListパラメータを持つコンバージョンを拡張してコンバージョンのチェーンを表し、convertメソッドを実装する必要があります。これにより、AutoConversionsは実際のコンバージョンのリストを提供するだけです。 私はここで私の最初の試みで、あなたはpathオーバーfoldでこれを実装することができると思います:暗黙のフォルダが見つからないので、

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

これは動作しません、だから私はコンパイラのタイプ情報をどこかに提供する必要があると推測しているが、わからない。

答えて

9

より多くの型情報が必要であるということは普通で、値はHList静的型の場合は、アプローチを変更する必要があります。基本的にHListで何もできないのは、それがHList(それに値を追加する以外に)だと知っていれば、通常はタイプ制限としてHListと書いてあります。

あなたの場合は、タイプアライメントされたシーケンスの一種です。このアプローチで前進する前に、実際にあなたが実際に必要としていることを本当に確かめることをお勧めします。機能(とConversionのような機能のようなタイプ)についての良い点の1つは、A => BB => Cがあり、A => Cにそれらを合成して、Bを永遠に忘れることができるということです。あなたはきれいなクリーンなブラックボックスを手に入れることができます。

しかし、場合によっては、パイプラインの部分を反映できるような方法で機能的なものを構成できることが有用な場合もあります。私はこれがこれらのケースの1つであると仮定しようとしていますが、それはあなた自身で確認する必要があります。そうでなければ、あなたは運がいい、何が来るかはまあまあです。

私はこれらの型を仮定します:

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 
} 

我々は、特定のHListは、その種類に並ぶ一回の以上の変換で構成されている証人という型クラスを定義することができますパイプラインに関するすべてのタイプ情報を含み、IおよびOがそのエンドポイントのタイプです。

次の私たちは、このタイプのクラスのインスタンス(これはcompanionedされる2つのための上記の特性と一緒に定義されなければならないことに注意してください)必要があります。

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

そして今、あなたのAutoConversionのバージョンを書くことができますがパイプラインについての型情報のすべてを追跡します:

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

そして、あなたはこのようにそれを使用することができます:

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) 

およびconvは、予想される静的タイプ(およびConversion[AutoA, AutoC]を実装)を持ちます。

関連する問題