2014-12-03 3 views
7

В принципе, я хотел бы быть в состоянии написать что-то вроде этого:Scala: есть ли способ создания встроенных типов?

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

Где типы х и у не совместимы, но либо делить супер типа или могут быть аннотированный с контекстными границами, позволяя мне чтобы сделать что-то вроде этого:

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

Ключевые слова используются здесь только в качестве примера, цель состоит в том, чтобы быть в состоянии обеспечить как литералы и одноэлементные типов для некоторых идентификаторов/ключевых слов/строк. Типы также могут быть стерты/унифицированы во время выполнения, меня интересует только проверка статического типа. Я думаю, это должно быть возможно, чтобы достичь этого с помощью макросов, но я бы предпочел.

+3

Я думаю, вы хотите что-то вроде [литеральных одноэлементных типов] (http://docs.scala-lang.org/sips/pending/42.type.html). –

+0

Который, IIRC, доступен в [вилке Typelevel] (https://github.com/typelevel/scala) компилятора Scala. –

+0

Альтернативно, [бесформенные записи] (https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#extensible-records), что вы хотите? – lmm

ответ

1

Вы можете построить структурные типы инлайн:

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 
     ^

Таким образом, вы можете объединить их с объектами, чтобы дать им типа уникальность:

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 (немного медленнее Beacause отражения)

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) 

Вы можете комбинировать его с tags аннотировать типа, как:

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] 

Больше примеров мечения:

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) 

Таким образом, вы можете:

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) 

Или просто используйте:

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 

Вы можете работать x, как и Int использованием Tag.unwrap(x), или просто определить implicit def t[T] = Tag.unwrap[Int, T] _ не делать различия между Tag и Int, но будьте осторожны - любой не-тег ориентированной функция удалит тег)

Другой конструктор решения рядного типа:

а) некрасиво

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) 

б) смешное:

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

Спасибо, пара хороших идей, чтобы разобраться. Мне особенно нравится последний, поскольку он имеет мало синтаксических накладных расходов, не использует никакой библиотеки и достаточно прост, чтобы понять, кто-то видит его в первый раз, надеясь передать «не слишком умный» тест. BTW, есть ли способ сказать компилятору использовать нотацию infix при печати типа? Сейчас единственная проблема этого решения заключается в том, что ошибки являются загадочными как ад. Я закончил использовать только простые структурные типы в своем коде, что не позволяло гибкости, я хотел и был немного многословным, но, по крайней мере, eqsy, чтобы понять. – Turin

+0

Yrw! не нашел способ переключиться на нотацию infix (исключая плагин компилятора или (?) макрос). я немного изменил значение, теперь он печатает: 'abcabcabc res22: - [- [- [- [- [- [- [- [single [a _], b _], c _], a _], b_] , c_], a _], b_], c_] ' – dk14

+0

Хех, я просто печатал то же самое решение, но с планшета, так что вы были быстрее :) Еще раз спасибо, это выглядит круто. – Turin

0

Итак, чтобы оставаться простым, что с этим делать?

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

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

Хорошо, я обманул, это действительно не встроенный, но я думаю, что он достаточно короткий для ваших нужд. Однако, если вы хотите сохранить значение в ХТ или уг, вам придется использовать что-то более длинный: object xt {val get = 'k1}

Смежные вопросы