2014-12-03 81 views
7

基本上,我希望能夠寫這樣的事:Scala:有沒有辦法創建內聯類型?

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

凡類型x和y的不兼容,但無論是共享的超類型或通過上下文邊界進行標註,讓我做這樣的事情:

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

關鍵詞用在這裏只是作爲一個例子,我們的目標是能夠爲一些標識符/關鍵字/串同時提供文字和單類型。這些類型可能會在運行時被擦除/統一,我只對靜態類型檢查感興趣。我想應該可以使用宏來實現,但我寧願不要。

+3

我想你想要的東西像[基於字面值的單例類型](http://docs.scala-lang.org/sips/pending/42.type.html)。 – 2014-12-03 14:48:33

+0

Scala編譯器的[Typelevel's fork](https://github.com/typelevel/scala)中提供了IIRC。 – 2014-12-03 14:54:14

+0

或者,[形狀記錄](https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#extensible-records)你想要什麼? – lmm 2014-12-03 16:05:08

回答

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 

或(東陽反射的稍微慢一點)

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如同時使用IntTag.unwrap(x),或只是定義implicit def t[T] = Tag.unwrap[Int, T] _,使標籤和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

謝謝,幾個好主意來解決。我特別喜歡最後一個,因爲它沒有任何語法開銷,不使用任何庫,並且很容易第一次看到它,希望通過'不太聰明'的測試。順便說一句,有沒有辦法告訴編譯器在打印類型時使用中綴表示法?現在這個解決方案的唯一問題是錯誤如同地獄一樣神祕。我最終在我的代碼中使用了簡單的結構類型,它不允許我想要的靈活性,並且有點冗長,但至少是易於理解的。 – Turin 2014-12-28 17:06:58

+0

Yrw!沒有找到切換到中綴表示法(不包括編譯器插件或(?)宏)的方法。我已經改變了一點,現在它打印出來了:'abcabcabc res22: - [ - [ - [ - [ - [single [a _],b _],c _],a _],b_] ,c _],a _],b _],c_]' – dk14 2014-12-28 17:38:12

+0

呵呵,我只是輸入完全相同的解決方案,但是從平板電腦上,所以你更快:)再次感謝,這看起來很酷。 – Turin 2014-12-28 17:59:52

0

因此,爲了保持簡單,這個怎麼樣?

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

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

好吧我欺騙了,它不是真的內聯,但我認爲它足夠短,可以滿足您的需求。 但是,如果你想存儲在XT或YT一個值,你將不得不使用一些較長的: object xt {val get = 'k1}

相關問題