2014-12-03 25 views
7

Zasadniczo chciałbym być w stanie napisać coś takiego:Scala: czy istnieje sposób tworzenia typów wbudowanych?

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

Gdzie typów X i Y nie są kompatybilne, ale też dzielić się super typ lub mogą być opatrzone granice kontekstowych, pozwalając mnie zrobić coś takiego:

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

słowa kluczowe są wykorzystywane jedynie jako przykład, celem jest, aby być w stanie zapewnić zarówno literały i dla niektórych typów singleton identyfikatory/słów kluczowych/strun. Typy mogą również zostać usunięte/ujednolicone w czasie wykonywania, interesuje mnie tylko statyczna kontrola typów. Sądzę, że powinno być możliwe osiągnięcie tego przy pomocy makr, ale wolałbym nie.

+3

Podejrzewam, że chcesz coś takiego jak [literalne typy singleton] (http://docs.scala-lang.org/sips/pending/42.type.html). –

+0

Jakie są, IIRC, dostępne w [widelec Typelevel] (https://github.com/typelevel/scala) kompilatora Scala. –

+0

Alternatywnie, są [bezkształtne rekordy] (https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#extensible-records), czego chcesz? – lmm

Odpowiedz

1

Można skonstruować strukturalnej typy inline:

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 
     ^

Tak, można je połączyć z obiektów, aby dać im wpisać wyjątkowość:

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 

lub (nieco wolniejsze beacause refleksji)

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) 

Można łączyć je z tags opisywanie typ jak:

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] 

Więcej przykładów tagowanie:

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) 

tak, możesz:

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 po prostu użyj:

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 

może działać x zarówno jako Int użyciu Tag.unwrap(x), lub po prostu zdefiniować implicit def t[T] = Tag.unwrap[Int, T] _ dokonać żadnej różnicy między Tag i Int, ale należy zachować ostrożność tutaj - każda funkcja zorientowane non-tag usunie tag)

inny rodzaj rozwiązania rolki konstruktor

a) brzydki

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) śmieszne:

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

Dzięki, kilka dobrych pomysłów na łamigłówkę. Szczególnie podoba mi się ten ostatni, ponieważ ma niewielki wpływ na składnię, nie korzysta z żadnej biblioteki i jest wystarczająco prosty, aby zrozumieć go ktoś, kto go widział po raz pierwszy, miejmy nadzieję, że przejdzie "niezbyt mądry" test. BTW, czy istnieje sposób, aby powiedzieć kompilatorowi, aby używał notacji typu infix podczas drukowania typu? W tej chwili jedynym problemem tego rozwiązania jest to, że błędy są tajemnicze jak diabli. Skończyło się na używaniu zwykłych typów strukturalnych w moim kodzie, co nie pozwalało na elastyczność, jakiej chciałem, i było trochę gadatliwe, ale co najmniej do zrozumienia. – Turin

+0

Yrw! nie znalazłem sposobu przełączenia na notację infiksową (z wyjątkiem wtyczki kompilatora lub makra (?)). trochę się zmieniłem, teraz drukuje: 'abcabcabc res22: - [- [- [- [- [- [- [- [single [a _], b _], c _], a _], b_] , c _], _ _, b _], c_] ' – dk14

+0

Heh, właśnie pisałem dokładnie to samo rozwiązanie, ale z tabletu, więc byłeś szybszy :) Jeszcze raz dziękuję, to wygląda fajnie. – Turin

0

Tak więc, aby pozostać prostym, co z tym zrobić?

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

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

Okay Oszukałem, to nie jest tak naprawdę w linii, ale myślę, że jest wystarczająco krótki, by spełnić twoje potrzeby. Jednakże, jeśli chcesz przechowywać wartość w xt i yt, będziesz musiał użyć czegoś dłuższego: object xt {val get = 'k1}