2017-08-11 5 views
15

Ich verwendete =:= als Beispiel Typ Lambda für den Zweck, einfache minimales Beispiel zu machen.Wie definiere ich Lambda richtig?

=:= Geben Sie zwei Argumente ein, ich möchte eine auf Typebene curry.

Ich nehme naive Umsetzung type Curry[G] = {type l[L] = L =:= G} aber in der Praxis verwendet es Fehler verursacht:

type X = Int 
type Y = Int 

type CurryInt[T] = T =:= Int 
type Curry[G] = {type l[L] = L =:= G} 
type CurrStatic = {type l[L] = L =:= Int} 
object CurryObj {type l[L] = L =:= Int} 

trait Apply[P[_], T] 
implicit def liftApply[P[_], T](implicit ev : P[T]) = new Apply[P,T] {} 

implicitly[Apply[CurryInt, Y]] // ok 
implicitly[Apply[Curry[X]#l, Y]] // fails 
implicitly[Apply[Curry[X]#l, Y]](liftApply) // fails 
implicitly[Apply[Curry[X]#l, Y]](liftApply[Curry[X]#l, Y]) // ok 
implicitly[Apply[CurrStatic#l, Y]] // fails 
implicitly[Apply[CurryObj.l, Y]] // ok 

Typ-Inferenz hier bricht. Wie sollte ich den Typ lambdas definieren, damit er funktioniert?

Antwort

2

etwas ausführlicher, aber kompiliert :) (scala 2.12.3)

type X = Int 
    type Y = Int 

    type CurryInt[T] = T =:= Int 
    type Curry[G] = {type l[L] = =:=[L, G]} 
    type CurrStatic = {type l[L] = L =:= Int} 
    object CurryObj {type l[L] = L =:= Int} 

    trait Apply[P[_], T] 
    implicit def liftApply[P[_], T](implicit ev : P[T]) = new Apply[P,T] {} 


    type L1[R] = =:=[R, X] 
    type L2[R] = =:=[R, Int] 
    implicitly[Apply[CurryInt, Y]] // ok 
    implicitly[Apply[L1, Y]] // ok 
    implicitly[Apply[L1, Y]](liftApply[L1, Y]) // ok 
    implicitly[Apply[Curry[X]#l, Y]](liftApply[Curry[X]#l, Y]) // ok 
    implicitly[Apply[L2, Y]] // ok 
    implicitly[Apply[CurryObj.l, Y]] // ok 
+0

kurz: verwendet keine Typprojektion.Die Typdeklaration darf nicht gebundene Variablen haben – ayvango

+0

Sie waren nicht spezifisch bezüglich Ihrer Anforderungen, aber diese Lösung kompiliert, warum versuchen Sie, mit der 'Typprojektion' zu gehen? Ist es nur eine Übung? – pedromss

+0

Ich war mir nicht bewusst, dass nicht gebundene Typen im Ausdruck type erlaubt sind. Also habe ich versucht in der Typenebene etwas wie 'λx.λy.x == y' zu kodieren. Ich könnte mir nicht vorstellen, dass das einfache 'λx.x == y 'der korrekte Ausdruck ist. – ayvango

3

diese vereinfachte Version Ihres Beispiel vor:

trait Secret 
type Curry = { type l[L] = Secret } 

def foo[P[_], T](ev : P[T]) = ??? 
val bar: Curry#l[Int] = ??? 

foo(bar) 

Wenn foo Aufruf der Wert bar ist einfach vom Typ Secret weiß der Compiler nicht woher Ihr bestimmter Secret stammt.

Ihr bar Wert ist nur ein Secret, und es verwaltet keine Informationen, die auf Curry#l[Int] zurückgehen.

Der Compiler kann nicht darauf schließen, dass P => Curry#l und T => Int.

Der Compiler sieht nur die Secret und verliert den Curry#l Kontext trotz der Art mit Curry#l[Int] statt Secret mit Anmerkungen versehen.

Ein weiteres Beispiel (aus this Frage kommen), ein ähnliches Verhalten auszusetzen:

trait Curry { type l } 
trait CurryB extends Curry { type l = String } 

def foo[P <: Curry](x: P#l) = ??? 
val bar: CurryB#l = ??? 

foo(bar) 

CurryObj Situation anders ist, der Ansicht, dass CurryInt#l, Curry#l und CurrStatic#l sind nur Aliase geben. CurryObj.l ist stattdessen ein tatsächlicher Typ, Teil des konkreten Objekts CurryObj.

Lassen Sie uns einen Blick auf dieses (REPL) haben:

scala> trait Secret 
defined trait Secret 

scala> type Curry = { type l[L] = Secret } 
defined type alias Curry 

scala> object CurryObj { type l[L] = Secret } 
defined object CurryObj 

scala> object S extends Secret 
defined object S 

scala> val foo0: Curry#l[Int] = S 
foo0: Secret = [email protected] 

scala> val foo1: CurryObj.l[Int] = S 
foo1: CurryObj.l[Int] = [email protected] 

Beachten Sie, dass der Typ alias Curry#l[Int] ->Secret sofort behoben wird, anstatt den tatsächlichen Typ CurryObj.l[Int] gehalten wird.

+0

Typ 'Curry' ist wie' CurrStatic' von meinem Beispiel. Es sieht "CurryObj" sehr ähnlich. Warum werden die Typinformationen in einem Fall gespeichert und in einem anderen gelöscht? – ayvango

+0

Ich habe die Antwort aktualisiert, um auf Ihre Bedenken einzugehen. –

+0

Es hat Bedeutung. Aber in meinem Fall wird 'Curry # l' verwendet, ohne' Int' zu verwenden. Und "Secret" hat auch einen Typparameter, so dass es nicht eliminiert werden konnte. Wie ich in '-Yinfer-debug' gesehen habe, wird der Typ-Parameter beibehalten, aber in einer ungewöhnlichen Form – ayvango

0

Es scheint, dass Scala-Compiler nicht mit nackten Typen von Typ Projektionen umgehen konnte. Ich verfolgt -Ytyper-debug Ausgabe und festgestellt, dass alle benötigten Informationen Typ wird weggetragen, aber ohne ersichtlichen Grund abgelehnt wird. Aber es ist immer noch möglich, lamplas-Wrapping-Ausdrücke innerhalb eines Merkmals zu erhalten. That answer gab mir einen Einblick in die Lösung.

type X = Int 
type Y = Int 

trait Wrap { 
    type l[T] 
} 
trait GenWrap[W[_]] extends Wrap { 
    final type l[T] = W[T] 
} 

type CInt[T] = T =:= Int 
class CurryInt extends Wrap {type l[T] = T =:= Int} 
class Curry[U] extends Wrap {type l[T] = T =:= U} 
type TCurry[U] = Wrap {type l[T] = T =:= U} 

trait Apply[W <: Wrap, T] 
implicit def lift[W <: Wrap, T](implicit ev : W#l[T]) = new Apply[W,T] {} 

implicitly[Apply[CurryInt, Y]] 
implicitly[Apply[Curry[X], Y]] 
implicitly[Apply[TCurry[X], Y]] 
implicitly[Apply[GenWrap[CInt], Y]]