2014-12-03 11 views
7

Fondamentalmente, mi piacerebbe essere in grado di scrivere qualcosa del genere:Scala: c'è un modo per creare tipi in linea?

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

Dove tipi di X e Y non sono compatibili, ma in entrambi condividono un super tipo o possono essere annotate con il contesto limiti, permettendomi di fare qualcosa di simile:

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

le parole chiave sono utilizzati qui solo come esempio, l'obiettivo è quello di essere in grado di fornire sia letterali e tipi di Singleton per alcuni identificatori/parole chiave/stringhe. I tipi potrebbero anche essere cancellati/unificati in runtime, mi interessa solo il controllo di tipo statico. Immagino che dovrebbe essere possibile ottenere questo usando i macro, ma preferirei di no.

+3

Immagino che tu voglia qualcosa come [tipi singleton letterali (http://docs.scala-lang.org/sips/pending/42.type.html). –

+0

Quali sono, IIRC, disponibili in [Typelevel's fork] (https://github.com/typelevel/scala) del compilatore Scala. –

+0

In alternativa, sono [record informi] (https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#extensible-records) cosa vuoi? – lmm

risposta

1

È possibile costruire tipologie strutturali in linea:

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 
     ^

Quindi, è possibile combinarli con gli oggetti per dare loro tipo unicità:

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 

Or (po 'più lento beacause di riflessione)

0.123.

È possibile combinare con tags per annotare il tipo come:

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] 

Più esempi di tagging:

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) 

Quindi, è possibile:

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 basta usare:

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 

È possibile far funzionare x sia come Int utilizzando Tag.unwrap(x), o semplicemente definire implicit def t[T] = Tag.unwrap[Int, T] _ fare alcuna differenza tra tag e Int, ma attenzione qui - qualsiasi funzione non tag orientato rimuoverà il tag)

un altro soluzioni di tipo inline costruttore:

a) brutta

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) divertenti:

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

Grazie, un paio di buone idee da risolvere. In particolare mi piace l'ultimo, visto che ha poco overhead sintattico, non usa alcuna libreria ed è abbastanza semplice da capire da qualcuno che lo vede per la prima volta, sperando di superare il test 'non troppo intelligente'. A proposito, c'è un modo per dire al compilatore di usare la notazione infisso quando si stampa il tipo? In questo momento l'unico problema di questa soluzione è che gli errori sono criptici da morire. Ho finito per usare solo semplici tipi strutturali nel mio codice, che non permettevano la flessibilità che volevo ed erano un po 'prolissi, ma almeno eqsy da capire. – Turin

+0

Yrw! non ha trovato un modo per passare alla notazione infix (escludendo il plugin del compilatore o la macro (?)). ho cambiato un po 'impl, ora stampa: 'abcabcabc res22: - [- [- [- [- [- [- [- [singolo [a _], b _], c _], a _], b_] , c _], a _], b _], c_] ' – dk14

+0

Heh, stavo solo scrivendo la stessa identica soluzione, ma da un tablet, così sei stato più veloce :) Grazie ancora, questo aspetto è fantastico. – Turin

0

Quindi, per rimanere semplici, che dire di questo?

Ok, ho barato, non è veramente in linea, ma penso che sia abbastanza breve per le vostre necessità. Tuttavia, se si desidera memorizzare un valore in xt o yt, dovrete usare qualcosa più: object xt {val get = 'k1}

Problemi correlati