2017-05-24 10 views
3

私は内部DSLを書いています。型の安全を守るためにShapelessを使っています。しかし、私は問題を抱えている。シェイプレス:多型関数の型制約のチェック

問題の簡略版は次のとおりです。

は、以下のコードを検討:

import shapeless._ 
import syntax.std.function._ 
import ops.function._ 

implicit class Ops[P <: Product, L <: HList](p: P)(implicit val gen: Generic.Aux[P, L]) { 
    def ~|>[F, R](f: F)(implicit fp: FnToProduct.Aux[F, L ⇒ R]) = 
    f.toProduct(gen.to(p)) 
} 

(1, 2) ~|> ((x: Int, y: Int) ⇒ x + y) // -> at compile time, it ensures the types are aligned. 

(1, 2, 3) ~|> ((x: Int, y: Int, z: Int) ⇒ x + y + z) // -> compiles okay 

(1, "2", 3) ~|> ((x: Int, y: Int, z: Int) ⇒ x + y + z) // -> gives a compile error, as expected. 

の代わりにAのしかし、私は、コンテナタイプPlace[A]を使用したいです。

case class Place[A](a: A) 

val a = Place[Int](1) 
val b = Place[Int]("str") 

であり、タイプパラメータに対してタイプが揃っていることを保証します。ある

(a, b) ~|> ((x: Int, y: String) ⇒ x.toString + y) 

、上記の場合において、Iは、それぞれどのような場合において、IntString、[_]プレースの型パラメータに基づいてチェックするタイプたいです。

非常に助けていただきありがとうございます。

+0

ちょうどヒントです。私は現在、それを試す時間がありません。 hlistの制約が役立つかもしれません:https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/hlistconstraints.scala –

+0

あなたの入力 'P <:Product'型の' 「A」になるだろうか?コンテナで具体的に何をしようとしていますか? – flavian

+0

@flavianでは、以前の計算のために、値は 'Place [_] 'で幾分既に持ち上げられています。この場合、型が整列していることを確認したいと思います。これに加えて、関数を後で(最初の例とは異なり)解釈できるように、関数をコンテナに持ち上げることを考えています。 –

答えて

2

これは、UnwrappedLiftAllの組み合わせで行うことができます。

UnwrappedAnyVal,LiftAllの内容を抽出して、HListのすべてのエントリに対して特定のタイプのクラスを呼び出すことができます。ここで私はそれが適切でタイプセーフ作り、それをシンプルに保つためにComappedと危険なジップ/マップを使用していたことを

case class Place[A](a: A) extends AnyVal // cuz why not? 

implicit class Ops[P <: Product, L <: HList, U <: HList, C <: HList](p: P) 
    (implicit 
    val g: Generic.Aux[P, L], 
    val l: LiftAll.Aux[Unwrapped, L, U], 
    val c: Comapped.Aux[Place, L, C] 
) { 
    def ~|>[F, R](f: F)(implicit fp: FnToProduct.Aux[F, C ⇒ R]) = { 
     def fuckitZip(l1: HList, l2: HList): HList = (l1, l2) match { 
     case (h1 :: t1, h2 :: t2) => (h1, h2) :: fuckitZip(l1, l2) 
     case _ => HNil 
     } 
     def fuckitMap(l: HList, f: Any => Any): HList = l match { 
     case HNil => HNil 
     case h :: t => f(h) :: fuckitMap(t, f) 
     } 
     def f(a: Any): Any = a match { 
     case (x: Any, y: Any) => 
      x.asInstanceOf[Unwrapped[Any] { type U = Any }].unwrap(y) 
     } 
     val zp = fuckitZip(g.to(p), l.instances) 
     val uw = fuckitMap(zp, f _).asInstanceOf[C] 
     f.toProduct(uw) 
    } 
} 

注:私はあなたがやろうとしているかを正しく理解していれば、それは次のようになります。 HListジップ/マップは練習問題として残されています。私はこれが基本操作:)

となんとかであることを示したかった、通常、これらの複雑な変換と、それは単純かもしれません(そしてより高速/コンパイラを実行します)のよう

すべてをインライン化することで、専用の型クラスですべてを再実装するために、

+0

これはあまりにも面白いですし、うまくいきました+100。 HListからの再帰的な暗黙の導出を使用してマップを型定義することもできます。 – flavian

関連する問題