2016-08-14 5 views
3

は、私は型崩れの記録を持っていると言う:デフォルト値でシェイプレスレコードを作成するにはどうすればよいですか?

trait T 
case object Ti extends T 
case object Ta extends T 
case object To extends T 

type R = Record.`Ta -> Option[String], Ti -> Option[Int]`.T 
val r: R = (Ta ->> Option("plif")) :: (Ti ->> Option(4)) :: HNil 

私はgetように関数を記述したいと思います:

get(Ta) == Some("plif") 
get(Ti) == Some(4) 
get(To) == None 

これを達成するための最良の方法だろうか?

+0

クラス「X」はどのように見えますか?ところで、すべての必要なインポートステートメントを提供するときにコードを試す方が簡単です。 – devkat

+0

私は質問を簡略化して、重要な部分に焦点を当てています。うまくいけば、それは簡単に答えるはずです。 – arnfred

+0

それはあなたの問題を解決しませんが、あなたの元の質問については: 'X'では' K'だけ 'k 'をタイプするので、' V'と 'M'は知られていないので、暗黙のうちに。しかし、合併はすぐに 'V 'を使用しようとするので、うまくいきません。私はそれを次のようにコンパイルすることに成功しました: 'クラスX [K <:T、OV、V、M <:HList>(val k:K)(暗黙のs1:Selector.Aux [AR、K、オプション:[M]、[K]、[V]、オプション:[V] )はA [K、V] 'を拡張する。 's1'は' R'から 'V'を計算し、' ev'は 'Option'を展開して、'合併 'と 's'に行きます。 – Kolmar

答えて

1

簡単な解決策は、デフォルトの場合のために、独自のSelectorインスタンスを提供することです:

class DefaultSelector[R <: HList, K] extends Selector[R, K] { 
    type Out = Option[Nothing] 
    def apply(l: R): Out = None 
} 

def get[K, V](k: K)(
    implicit sel: Selector[R, K] = new DefaultSelector[R, K] 
): sel.Out = sel(r) 

しかし、そのコードでScalaのコンパイラーは、デフォルトの場合の結果のためにTypeTag Sを提供する難しさを有することができます。だから、何Selectorが見つからない場合にも、None: Option[Nothing]にデフォルト設定されます新しい型クラスDefaultSelectorを、書くことができることを修正するための


import shapeless._ 
import shapeless.ops.record._ 

trait DefaultSelector[R <: HList, K] { 
    type Out 
    def apply(r: R): Out 
} 

sealed trait LowPriorityDefaultSelector { 
    type Aux[R <: HList, K, V] = DefaultSelector[R, K] { type Out = V } 

    case class Impl[R <: HList, K, V](get: R => V) extends DefaultSelector[R, K] { 
    type Out = V 
    def apply(r: R): Out = get(r) 
    } 

    implicit def default[R <: HList, K, V](
    implicit ev: Option[Nothing] =:= V // tricking Scala's implicit resolution 
): Aux[R, K, V] = 
    Impl[R, K, V](Function.const(None)) 
} 

object DefaultSelector extends LowPriorityDefaultSelector { 
    implicit def existing[R <: HList, K, V](
    implicit sel: Selector.Aux[R, K, V] 
): Aux[R, K, V] = 
    Impl[R, K, V](sel.apply) 
} 

その後get関数は次のようになります。

def get[K, V](k: K)(implicit sel: DefaultSelector[R, K]): sel.Out = sel(r) 

結果(両方のソリューション)次のとおりです:

scala> get(Ti) 
res0: Option[Int] = Some(4) 

scala> get(Ta) 
res1: Option[String] = Some(plif) 

scala> get(To) 
res2: Option[Nothing] = None 
+0

これはとても素晴らしい解決策ですが、=:=を避ける方法があるのだろうかと思います。オペレータは並列アプリケーションをブロックしているので、パフォーマンスがかなり低下します。 – arnfred

+0

値がレコードにない場合でもリターンキータイプを知っている傾向があることが分かります。そのため、最初の解決方法は本当に素敵で簡単な方法です。暗黙的な値にデフォルト値を提供することが可能であることは私には決してありませんでした。 – arnfred

+0

@arnfred説明してください、なぜ、= = =がブロックされていますか? (R、K、Option [Nothing])(Function.const();}これは、デフォルトでは、 None)) ')。しかし、これはScalaのオーバーロードされた暗黙の解決の癖のためにコンパイルされません。だから、仕様の§6.26.3の定義によれば、「デフォルト」が「あまり具体的でない」必要があり、それを行うためのより良い方法が思いつきませんでした。 – Kolmar

関連する問題