Scala: is there a way to create inline types?

Basically, I would like to write something like this:

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

Where the types x and y are incompatible, but either have a common type, or can be annotated using context boundaries, which allows me to do something like this:

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

The keywords are used here only as an example, the goal is to provide both literals and singleton types for some identifiers / keywords / strings. Types can also be erased / unified at runtime, I'm only interested in checking static types. I think it should be possible to achieve this with macros, but I would prefer.

+7
types scala type-systems inline singleton-type
source share
2 answers

You can build inline structure types:

 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) = ( $anon$1@68e2da47 ,1) scala> var b = new {def hello = null} -> 2 b: (AnyRef{def hello: Null}, Int) = ( $anon$1@77147ad6 ,2) scala> b = a b: (AnyRef{def hello: Null}, Int) = ( $anon$1@68e2da47 ,1) scala> var c = new {def helloooo = null} -> 1 c: (AnyRef{def helloooo: Null}, Int) = ( $anon$1@38def4a2 ,1) scala> c = a <console>:15: error: type mismatch; found : (AnyRef{def hello: Null}, Int) required: (AnyRef{def helloooo: Null}, Int) c = a ^ 

So, you can combine them with objects to give them unique types:

  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 (a little slower beacause of reflection)

 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) 

You can combine it with tags to annotate your type, for example:

 import scalaz._ import Scalaz._ scala> def markALittle[T](a: T) = Tag[T, MyLittleType](a) markALittle: [T](a: T) scalaz.@ @[T,MyLittleType] scala> markALittle(new {def hello: Aaa = null}) res15: scalaz.@ @[AnyRef{def hello: Aaa},MyLittleType] = $anon$1@a8c48e8 

Additional tag examples:

 scala> trait MyLittleType scala> trait Spike extends MyLittleType; val x = Tag[Symbol, Spike]('k1) -> 1 x: ( scalaz.@ @[Symbol,Spike], Int) = ('k1,1) scala> trait Rainbow extends MyLittleType; val y = Tag[Symbol, Rainbow]('k2) -> 1 y: ( scalaz.@ @[Symbol,Rainbow], Int) = ('k2,1) scala> val y: ( scalaz.@ @[Symbol,Spike], Int) = Tag[Symbol, Rainbow]('k1) -> 1 <console>:22: error: type mismatch; found : ( scalaz.@ @[Symbol,Rainbow], Int) required: ( scalaz.@ @[Symbol,Spike], Int) val y: ( scalaz.@ @[Symbol,Spike], Int) = Tag[Symbol, Rainbow]('k1) -> 1 scala> val z: ( scalaz.@ @[Symbol,_ <: MyLittleType], Int) = Tag[Symbol, Rainbow]('k1) -> 1 z: ( scalaz.@ @[Symbol, _ <: MyLittleType], Int) = ('k1,1) 

So you can:

 scala> def mlt[T <: MyLittleType](x :( scalaz.@ @[Symbol,T], Int)) = x mlt: [T <: MyLittleType](x: ( scalaz.@ @[Symbol,T], Int))( scalaz.@ @[Symbol,T], Int) scala> mlt(x) res42: ( scalaz.@ @[Symbol,Spike], Int) = ('k1,1) scala> mlt(y) res43: ( scalaz.@ @[Symbol,Rainbow], Int) = ('k2,1) 

Or just use:

 scala> val x = Tag[Int, Rainbow](1) x: scalaz.@ @[Int,Rainbow] = 1 scala> val y = Tag[Int, Spike](1) y: scalaz.@ @[Int,Spike] = 1 

You can use x as Int with Tag.unwrap(x) or just define implicit def t[T] = Tag.unwrap[Int, T] _ so that there is no difference between a tag and Int, but be careful: any function, not tag-oriented, remove the tag)

Other built-in constructor solutions:

a) ugly

 scala> class ___ defined class ___ scala> class __[T,U] extends ___ defined class __ scala> val x = Tag[Symbol, ___ __ ___]('k1) -> 1 x: ( scalaz.@ @[Symbol,__[___,___]], Int) = ('k1,1) scala> var y = Tag[Symbol, ___ __ ___ __ ___]('k1) -> 1 y: ( scalaz.@ @[Symbol,__[__[___,___],___]], Int) = ('k1,1) scala> y = x <console>:59: error: type mismatch; found : ( scalaz.@ @[Symbol,__[___,___]], Int) required: ( scalaz.@ @[Symbol,__[__[___,___],___]], Int) y = x ^ scala> def mlp[X <: scalaz.@ @[Symbol, _]](x: (X, Int)) = x mlp: [X <: scalaz.@ @[Symbol, _]](x: (X, Int))(X, Int) scala> mlp(x) res106: ( scalaz.@ @[Symbol,__[___,___]], Int) = ('k1,1) 

b) funny:

 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 = hello -> 1 x: (-[o_,-[l_,-[l_,-[h_,end[e_]]]]], Int) = ( $minus@350bc88 ,1) scala> var y = helloo -> 2 y: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) = ( $minus@46d7fdc0 ,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 = helloo -> 2 z: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) = ( $minus@6b899d5d ,2) scala> z = y z: (-[o_,-[o_,-[l_,-[l_,-[h_,end[e_]]]]]], Int) = ( $minus@46d7fdc0 ,2) 
+1
source share

So, to stay simple, how about this?

 object xt; val x = (xt, 1); object yt; val y = (yt, 2); def mlt(x: (_, Int)) = 42 mlt(x); mlt(y) 

Well, I cheated, it is not very important, but I think it is short enough for your needs. However, if you want to save the value in xt or yt, you will have to use something longer: object xt {val get = 'k1}

0
source share

All Articles