2017-05-13 3 views
3

Ich wollte tiefer in die Typ-Level-Programmierung in Scala einsteigen und begann mit ein paar kleinen Übungen. Ich begann mit einer Implementierung von Peano-Nummern auf der Typenebene. Hier ist der Code unten!Typ-Level-Programmierung in Scala

sealed trait PeanoNumType { // Type at the end indicates to the reader that we are dealing with types 
    type plus[That <: PeanoNumType] <: PeanoNumType 
} 

sealed trait ZeroType extends PeanoNumType { 
    type plus[That <: PeanoNumType] = That 
} 

sealed trait NextType[This <: PeanoNumType] extends PeanoNumType { 
    type plus[That <: PeanoNumType] = NextType[This#plus[That]] 
} 

Jetzt ist die Frage, was würde die obige Implementierung mich kaufen? Wie kann ich davon Gebrauch machen?

Antwort

6

Solange Sie diese Typen selbst erstellen müssen, gibt es nicht viel. Aber sobald Sie den Compiler das Zeug für Sie machen, wird es viel nützlicher sein.

Bevor ich dies zeigen, lassen Sie uns Peano aritmetic in etwas zu repräsentieren kürzer eine Art und Weise ändern:

sealed abstract class List[+H, N <: Num](val size: N) { 
    def ::[T >: H](value: T): List[T, Succ[N]] = Cons(value, this) 
} 
case object Nil extends List[Nothing, Zero.type](Zero) 
case class Cons[+H, N <: Num](head: H, tail: List[H, N]) extends List[H, Succ[N]](Succ(tail.size)) 
type ::[+H, N <: Num] = Cons[H, N] 

Wenn Sie:

sealed trait Num 
case object Zero extends Num 
case class Succ[N <: Num](num: N) extends Num 

Sie dann die Liste mit einer Größe zum Zeitpunkt der Kompilierung bekannt schaffen könnte überprüfen sie die Art von etw mit sych Liste erstellt wird es in seiner Art Größe codiert haben:

val list = 1 :: 2 :: 3 :: 4 :: Nil // List[Int, Succ[Succ[Succ[Succ[Zero.type]]]]] = Cons(1,Cons(2,Cons(3,Cons(4,Nil)))) 

Das nächste, was Sie versuchen könnten, wäre die Verwendung von implicits, um etwas zu überprüfen, z.

trait EvenNum[N <: Num] 
implicit val zeroIsEven = new EvenNum[Zero.type] {} 
implicit def evenNPlusTwo[N <: Num](implicit evenN: EvenNum[N]) = new EvenNum[Succ[Succ[N]]] {} 

Damit Sie, dass eine Operation nur durchgeführt werden könnte erzwingen könnte, wenn implizite Beweise geliefert werden könnten:

def operationForEvenSizeList[T, N <: Num](list: List[T, N])(implicit ev: EvenNum[N]) = { 
    // do something with list of even length 
} 

operationForEvenSizeList(1 :: 2 :: Nil) // ok 
operationForEvenSizeList(1 :: 2 :: 3 :: Nil) // compiler error 

Soweit ich wahre Macht der Typ-Level-Programmierung kann sagen, in Scala erscheint, wenn Sie beginnen mit der Verwendung von implicits, um neue Typen zu erstellen: solche, die Sie für den impliziten Beweis verwenden könnten, die Typklassenableitung und das Entfernen einiger Strukturtypen.

Eine Bibliothek, die viel mit generischer Programmierung hilft, ist Shapeless. Ich glaube, es wird eine lustige Sache für Sie sein, mit zu arbeiten, sobald Sie eine Übung oder zwei mit einer Typklassenableitung mit implicits machen werden.

Zurück zu Ihrem Code: Sie könnten einige implitits bereitstellen, die Instanzen Ihrer Klasse für Sie generieren und bereitstellen würden. Neben der Erstellung einer neuen Klasse würde dieser Code auch etwas anderes tun, z. Kombinieren Sie Listen von Elementen, die Sie in diesen Klassen hinzufügen würden, oder stellen Sie eine Umwandlung von PeanoNumType in Int bereit, oder fügen Sie einige Vergleichselemente hinzu, die während der Kompilierungszeit usw. arbeiten. Sky ist das Limit.