2014-12-03 15 views
7

Grundsätzlich würde Ich mag Lage sein, so etwas zu schreiben:Scala: Gibt es eine Möglichkeit, Inline-Typen zu erstellen?

val x :('k1.type, Int) = 'k1 -> 1 
val y :('k2.type, Int) = 'k2 -> 2 

Wo Typen von x und y nicht kompatibel sind, aber entweder ein Supertyp teilen oder mit Kontext Grenzen mit Anmerkungen versehen werden, mir erlaubt so etwas tun:

def mlt[T :MyLittleType](x :(T, Int)) = ??? 
mlt(x); mlt(y) 

Keywords werden verwendet, hier nur als Beispiel, ist das Ziel der Lage sein, beide Literale bieten und Singleton-Typen für einige Kennungen/Stichworte/Strings. Die Typen können auch in der Laufzeit gelöscht/vereinheitlicht werden, ich bin nur an der statischen Typprüfung interessiert. Ich denke, es sollte möglich sein, dies mithilfe von Makros zu erreichen, aber das möchte ich nicht.

+3

Ich denke, Sie wollen etwas wie [literal-basierte Singletontypen] (http://docs.scala-lang.org/sips/pending/42.type.html). –

+0

Welche sind, IIRC, verfügbar in [Typelevel Gabel] (https://github.com/typelevel/scala) des Scala-Compilers. –

+0

Alternativ sind [formlos Datensätze] (https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#extensible-records), was wollen Sie? – lmm

Antwort

1

Sie können Inline-Strukturtypen konstruieren:

scala> val a = new {def hello = null} -> 1 // by the way hello is accessible in a (but scala uses reflection for that) 
a: (AnyRef{def hello: Null}, Int) = ([email protected],1) 

scala> var b = new {def hello = null} -> 2 
b: (AnyRef{def hello: Null}, Int) = ([email protected],2) 

scala> b = a 
b: (AnyRef{def hello: Null}, Int) = ([email protected],1) 

scala> var c = new {def helloooo = null} -> 1 
c: (AnyRef{def helloooo: Null}, Int) = ([email protected],1) 

scala> c = a 
<console>:15: error: type mismatch; 
found : (AnyRef{def hello: Null}, Int) 
required: (AnyRef{def helloooo: Null}, Int) 
     c = a 
     ^

So, können Sie sie kombinieren können sie geben Einzigartigkeit mit Objekten zu geben:

new {def myTypeName = null} -> myObject //now your myObject tagged with 'myTypeName', but your methods should be aware about tuples 

def mlp(x: ((Any, YourObjectsType), Int)) = x 

Oder (etwas langsamer beacause der Reflexion)

scala> def mlp(x: ({def obj: Symbol}, Int)) = x._1.obj -> x._2 
warning: there were 1 feature warning(s); re-run with -feature for details 
mlp: (x: (AnyRef{def obj: Symbol}, Int))(Symbol, Int) 

scala> mlp(new { def a1 = null; def obj = 'a1 } -> 1) 
res18: (Symbol, Int) = ('a1,1) 

scala> mlp(new { def a2 = null; def obj = 'a2 } -> 1) 
res19: (Symbol, Int) = ('a2,1) 

Sie können es kombinieren mit tags Ihre Art mit Anmerkungen versehen wie:

import scalaz._ 
import Scalaz._ 

scala> def markALittle[T](a: T) = Tag[T, MyLittleType](a) 
markALittle: [T](a: T)[email protected]@[T,MyLittleType] 

scala> markALittle(new {def hello: Aaa = null}) 
res15: [email protected]@[AnyRef{def hello: Aaa},MyLittleType] = [email protected] 

Weitere Tagging Beispiele:

scala> trait MyLittleType 

scala> trait Spike extends MyLittleType; val x = Tag[Symbol, Spike]('k1) -> 1 
x: ([email protected]@[Symbol,Spike], Int) = ('k1,1) 

scala> trait Rainbow extends MyLittleType; val y = Tag[Symbol, Rainbow]('k2) -> 1 
y: ([email protected]@[Symbol,Rainbow], Int) = ('k2,1) 

scala> val y: ([email protected]@[Symbol,Spike], Int) = Tag[Symbol, Rainbow]('k1) -> 1 
<console>:22: error: type mismatch; 
found : ([email protected]@[Symbol,Rainbow], Int) 
required: ([email protected]@[Symbol,Spike], Int) 
     val y: ([email protected]@[Symbol,Spike], Int) = Tag[Symbol, Rainbow]('k1) -> 1 


scala> val z: ([email protected]@[Symbol,_ <: MyLittleType], Int) = Tag[Symbol, Rainbow]('k1) -> 1 
z: ([email protected]@[Symbol, _ <: MyLittleType], Int) = ('k1,1) 

So können Sie:

scala> def mlt[T <: MyLittleType](x :([email protected]@[Symbol,T], Int)) = x 
mlt: [T <: MyLittleType](x: ([email protected]@[Symbol,T], Int))([email protected]@[Symbol,T], Int) 

scala> mlt(x) 
res42: ([email protected]@[Symbol,Spike], Int) = ('k1,1) 

scala> mlt(y) 
res43: ([email protected]@[Symbol,Rainbow], Int) = ('k2,1) 

Or benutze einfach:

scala> val x = Tag[Int, Rainbow](1) 
x: [email protected]@[Int,Rainbow] = 1 

scala> val y = Tag[Int, Spike](1) 
y: [email protected]@[Int,Spike] = 1 

Sie arbeiten kann x als beide IntTag.unwrap(x), oder nur implicit def t[T] = Tag.unwrap[Int, T] _ definieren keinen Unterschied zwischen Tag und Int zu machen, aber hier vorsichtig sein - jede nicht-Tag-orientierte Funktion entfernen Sie den Tag)

eine weitere Inline-Typkonstruktor Lösungen:

a) hässlich

scala> class ___ 
defined class ___ 

scala> class __[T,U] extends ___ 
defined class __ 

scala> val x = Tag[Symbol, ___ __ ___]('k1) -> 1 
x: ([email protected]@[Symbol,__[___,___]], Int) = ('k1,1) 

scala> var y = Tag[Symbol, ___ __ ___ __ ___]('k1) -> 1 
y: ([email protected]@[Symbol,__[__[___,___],___]], Int) = ('k1,1) 

scala> y = x 
<console>:59: error: type mismatch; 
found : ([email protected]@[Symbol,__[___,___]], Int) 
required: ([email protected]@[Symbol,__[__[___,___],___]], Int) 
     y = x 
     ^

scala> def mlp[X <: [email protected]@[Symbol, _]](x: (X, Int)) = x 
mlp: [X <: [email protected]@[Symbol, _]](x: (X, Int))(X, Int) 

scala> mlp(x) 
res106: ([email protected]@[Symbol,__[___,___]], Int) = ('k1,1) 

b) lustig:

class - [B <: -[_, _], A <: symbolic[A]] (a: A, b: B) { 
    def -[T <: symbolic[T]](c: T) = new - (c, this) 
} 

trait symbolic[F <: symbolic[F]] { 
    def - [T <: symbolic[T]](b: T) = new - [single[F],T](b, new single(this.asInstanceOf[F])) 
} 

class single[T <: symbolic[T]](a: T) extends - [single[_],T](a, null) 

val a = new a_; class a_ extends symbolic[a_] 
val b = new b_; class b_ extends symbolic[b_] 
val c = new c_; class c_ extends symbolic[c_] 
... 

scala> val x = h-e-l-l-o -> 1 
x: (-[o_,-[l_,-[l_,-[h_,end[e_]]]]], Int) = ([email protected],1) 

scala> var y = h-e-l-l-o-o -> 2 
y: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) = ([email protected],2) 

scala> y = x 
<console>:13: error: type mismatch; 
found : (-[o_,-[l_,-[l_,-[h_,end[e_]]]]], Int) 
required: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) 
     y = x 
     ^

scala> var z = h-e-l-l-o-o -> 2 
z: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) = ([email protected],2) 

scala> z = y 
z: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) = ([email protected],2) 
+0

Danke, ein paar gute Ideen zu rätseln. Besonders gut gefällt mir die letzte, da es wenig syntaktischen Overhead hat, verwendet keine Bibliothek und ist einfach genug, um jemanden zu verstehen, ist es zum ersten Mal zu sehen, hoffentlich den Test ‚nicht zu klug‘ vorbei. Übrigens, gibt es eine Möglichkeit, dem Compiler mitzuteilen, beim Drucken des Typs die Infix-Notation zu verwenden? Im Augenblick ist das einzige Problem dieser Lösung, dass Fehler kryptisch sind. Am Ende habe ich nur einfache strukturelle Typen in meinem Code verwendet, die nicht die Flexibilität erlaubten, die ich wollte, und die ein wenig ausführlich waren, aber zumindest leicht zu verstehen waren. – Turin

+0

Yrw! habe keine Möglichkeit gefunden, in die Infix-Notation zu wechseln (Compiler-Plugin oder (?) -Macro ausgenommen). Ich habe impl ein wenig geändert, jetzt druckt er: 'abcabcabc res22: - [- [- [- [- [- [- [- [single [a _], b _], c _], a _], B_] , c _], a _], b _], c_] ' – dk14

+0

Heh, ich war gerade genau die gleiche Lösung eingeben, sondern aus einer Tablette, so dass Sie schneller :) wieder Dank waren, sehen diese cool. – Turin

0

Also, um einfach zu bleiben, was ist damit?

object xt; val x = (xt, 1); 
object yt; val y = (yt, 2); 

def mlt(x: (_, Int)) = 42 
mlt(x); mlt(y) 

Okay, ich habe betrogen, es ist nicht wirklich inline, aber ich denke, es ist kurz genug für Ihre Bedürfnisse. Wenn Sie jedoch einen Wert in xt oder yt speichern möchten, werden Sie länger etwas verwenden müssen: object xt {val get = 'k1}