2016-09-21 3 views
3

Die formlose Dokumentation erläutert, wie Sie mit polymorphen Funktionen eine Funktion erstellen, die Objekte in einem Containertyp einem anderen zuordnet. Aber was ist, wenn Sie Objekte aus ihrem Container auspacken möchten?Verwenden einer polymorphen Funktion zum Extrahieren eines Objekts aus Optionen

Ich habe einen hList von Optionen

val options = Some(1) :: Some("A") :: Some(3.5) :: HNil 

ich eine polymorphe Funktion wollen, die den Inhalt von jedem der Optionen extrahieren kann.

// This is incorrect: 
object uuu extends (Option ~> Any) { 
    def apply[T](l:Option[T]):T = { 
    l.get 
    } 
} 

Ist diese Funktion richtig wäre, würde ich das folgende Verhalten wollen:

options.map(uuu) // I want: 1 :: "A" :: 3.5 :: HNil 

Wie kann ich dies korrigieren, so dass meine polymorphe Funktion tatsächlich funktioniert?

Antwort

2

Es gibt ein paar Probleme hier. Der erste ist, dass der statische Typ Ihrer hlist Some darin statt Option hat, so dass die Beweise, dass Sie beweisen müssen, dass uuu über options zugeordnet werden kann nicht gefunden werden. Der schönste Weg, dies zu beheben, ist ein intelligentes Some Konstruktor zu definieren, liefert ein Option:

def some[A](a: A): Option[A] = Some(a) 

val options = some(1) :: some("A") :: some(3.5) :: HNil 

Sie könnten auch Typenannotationen zu Ihren ursprünglichen options hinzufügen oder uuu mit Some statt Option (die würde arbeiten ändern sein sicherer, aber vermutlich weniger nützlich für alles, was Sie erreichen wollen.

Jetzt kompiliert Ihr Code und tut etwas, aber nur wegen der etwas bizarren Tatsache, dass Any in Scala freundlich-polymorph ist. Wenn Sie F ~> G haben, müssen sowohl F als auch G Typkonstruktoren sein, die einen einzigen Typparameter verwenden, z. Option ~> List. Any nimmt keinen Typparameter, und doch funktioniert es, wegen dieser merkwürdigen Tatsache über die Scala-Sprache (die Any, zusammen mit Nothing, ist irgendwie polymorph und wird jeden Platz passen, wo Sie einen Typ benötigen, einen Typkonstruktor mit ein Parameter, ein Typkonstruktor mit einem Dutzend Parametern usw.).

So kompiliert es, aber es ist ziemlich nutzlos, da es eine Any :: Any :: Any :: HNil zurückgibt. Sie können dieses Problem beheben, indem Sie die Any in der natürlichen Transformation mit shapeless.Id ersetzt:

import shapeless._, shapeless.poly.~> 

def some[A](a: A): Option[A] = Some(a) 

val options = some(1) :: some("A") :: some(3.5) :: HNil 

object uuu extends (Option ~> Id) { 
    def apply[T](l: Option[T]): T = l.get 
} 

options.map(uuu) 

Id ist definiert als type Id[+T] = T -i.e., es ist die Identität Typkonstruktor, dass Sie die ungeöffneten Art gibt.

Diese Version beide compiliert und gibt Ihnen wieder ein nutzbringend typisierte Ergebnis, aber es ist immer noch nicht wirklich sicher, da, wenn Sie eine Karte über hlist mit Elementen, die None (zur Laufzeit) sind, können Sie eine NoSuchElementException bekommen. Es gibt nicht wirklich einen Weg um dies zu ändern, abgesehen von der Änderung Option ~> Id zu Some ~> Id, irgendwie Bereitstellung von Standardwerten, etc., die alle die Art der Operation ziemlich dramatisch ändern.

+0

Könntest du bitte erklären: "Irgendwelche sind in Scala irgendwie polymorph", Travis? –

+0

@KevinMeredith Nun, dieser Abschnitt erklärt die Grundlagen.Um etwas Konkreteres zu definieren, definiere 'def foo [F [_, _, _]] (f: F [Int, Int, Int]) = f' und versuche 'foo [Any] (1)'. –

Verwandte Themen