2017-02-03 3 views
2

ich versucht, einen typeclass zu schreiben, SumEq5, so dass seine HList-5 aufaddieren ersten beiden Felder des Typs Parameter:Compile-Time-Check auf Summe von Nat's?

trait SumEq5[A] 
object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5Ev[L <: HList, A <: Nat, B <: Nat](
    implicit hcons: IsHCons.Aux[L, A, B :: HNil], 
      ev: Sum.Aux[A, B, _5] 
): SumEq5[L] = new SumEq5[L] {} 
} 

Aber es scheint nicht zu funktionieren:

import shapeless._ 
import shapeless.nat._ 
import net.SumEq5 

scala> SumEq5[_0 :: _5 :: HNil] 
<console>:19: error: could not find implicit value for 
    parameter ev: net.SumEq5[shapeless.::[shapeless.nat._0,shapeless.:: 
     [shapeless.nat._5,shapeless.HNil]]] 
     SumEq5[_0 :: _5 :: HNil] 

Bitte geben mir einen Hinweis, warum _0 :: _5 :: HNil hat keinen Beweis, dass seine beiden Nat sind gleich 5.

EDIT

Aktualisierte Frage per Denis Rosca hilft in shapeless's gitter.

+0

Der Zug leer ist, warum also nicht nur ' neu SumEq5 [L] '? – cchantep

+0

Das ist in Ordnung, aber es wird nicht für meine Eingabe funktionieren, nicht? –

Antwort

3

Dale Wijnand und Marcus Henry sind in der Zeige richtige Richtung, wenn Sie HList s beliebiger Länge verallgemeinern wollen, aber wenn Sie wirklich nur zwei Element HList s aufnehmen wollen, dann ist das folgende eine eher einfachere Lösung,

scala> import shapeless._, nat._, ops.nat._ 
import shapeless._ 
import nat._ 
import ops.nat._ 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

trait SumEq5[A] 

object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5AB[A <: Nat, B <: Nat] 
    (implicit ev: Sum.Aux[A, B, _5]): SumEq5[A :: B :: HNil] = 
     new SumEq5[A :: B :: HNil] {} 
} 

// Exiting paste mode, now interpreting. 

defined trait SumEq5 
defined object SumEq5 

scala> SumEq5[_0 :: _5 :: HNil] 
res0: SumEq5[_0 :: _5 :: HNil]] = [email protected] 

der Hauptunterschied ist, dass die Instanz ist explizit de Für Listen mit zwei Elementen wird eine Geldstrafe verhängt, anstatt für Listen im Allgemeinen definiert zu werden, mit der Maßgabe, dass es einen Beweis dafür gibt, dass die Liste genau zwei Elemente enthält.

Dale Update folgen, können wir dies verallgemeinern HList s mit mindestens zwei (anstatt genau zwei) Elemente aufzunehmen, wieder ohne weitere Zeugen,

scala> import shapeless._, nat._, ops.nat._ 
import shapeless._ 
import nat._ 
import ops.nat._ 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

trait SumEq5[A] 

object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5AB[A <: Nat, B <: Nat, T <: HList] 
    (implicit ev: Sum.Aux[A, B, _5]): SumEq5[A :: B :: T] = 
     new SumEq5[A :: B :: T] {} 
} 

// Exiting paste mode, now interpreting. 

defined trait SumEq5 
defined object SumEq5 

scala> SumEq5[_0 :: _5 :: HNil] 
res0: SumEq5[_0 :: _5 :: HNil]] = [email protected] 
+0

In der Tat viel einfacher. Ich würde jedoch annehmen, dass die Idee war, einen HList aus 2 oder mehr Elementen zu handhaben, von denen die Summe der ersten beiden 5 ist. –

+0

Aktualisiert, um auch diesen Fall zu erfassen. –

+0

Oh ja, duh. Kevin, hier ist deine Antwort. –

3

Ich habe nur eine partielle Antwort für Sie, d. H. Eine (Problemumgehung) Lösung ohne zu verstehen, warum genau das Original nicht wie vorgesehen funktioniert.

scheint, dass Sie nicht direkt für einen IsHCons.Aux[L, A, B :: HNil] fragen können, können Sie es Stück für Stück tun müssen:

  1. IsHCons.Aux[L, A, L2] und dann
  2. IsHCons.Aux[L2, B, HNil]

Daher ist diese kompiliert:

import shapeless._, nat._, ops.hlist._, ops.nat._ 

trait SumEq5[A] 
object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5Ev[L <: HList, L2 <: HList, A <: Nat, B <: Nat](
    implicit hcons0: IsHCons.Aux[L, A, L2], 
      hcons: IsHCons.Aux[L2, B, HNil], 
      ev: Sum.Aux[A, B, _5] 
): SumEq5[L] = new SumEq5[L] {} 
} 

object T { 
    def main(args: Array[String]): Unit = { 
    SumEq5[_0 :: _5 :: HNil] 
    } 
} 

von Miles Sabin's answer folgend, kann diese eine beliebige hList von 2 oder mehr Elemente unterstützen gezwickt werden, von denen die Summe der beiden ersten 5 ist, etwa so:

import shapeless._, nat._, ops.hlist._, ops.nat._ 

trait SumEq5[A] 
object SumEq5 { 
    def apply[L <: HList](implicit ev: SumEq5[L]): SumEq5[L] = ev 

    implicit def sumEq5Ev[L1 <: HList, L2 <: HList, L3 <: HList, A <: Nat, B <: Nat](
    implicit hcons1: IsHCons.Aux[L1, A, L2], 
      hcons2: IsHCons.Aux[L2, B, L3], 
      ev: Sum.Aux[A, B, _5] 
): SumEq5[L1] = new SumEq5[L1] {} 
} 

object T { 
    def main(args: Array[String]): Unit = { 
    SumEq5[_0 :: _5 :: HNil] 
    } 
} 
+2

Ich sehe das oft, wenn Leute lernen, rekursive oder induktive Typen zu lernen. Der Compiler muss schrittweise gehen. Kevin, versucht, eine Instanz für A :: B :: HNil zu bekommen, ohne zuerst eine Instanz für B :: HNil zu haben, ohne vorher eine Instanz für HNil zu haben. Die Lösung hier vermeidet die Notwendigkeit eines induktiven Prozesses durch Hinzufügen einer weiteren IsHCons-Schicht. Um es induktiv zu machen, müssten Sie beweisen, dass B :: HNil kleiner als oder gleich _5 ist, dann ist A :: B :: HNil gleich _5 in zwei impliziten Defs. –

+0

Danke, Dale und Marcus! Diese Antwort und mein Kommentar haben mir definitiv geholfen, mehr zu lernen. –