2013-04-30 6 views
6

Mam nadzieję napisać metodę Scala, która pobiera krotkę dowolnego rozmiaru i typu wraz z indeksem i zwraca element w krotce w tym indeksie. Wiem, jak zrobić wszystko, ale zachować typ. Nie znalazłem jeszcze sposobu, aby zwrócić wartość dynamicznego typu krotki.Ogólna metoda gettera dla krotek w Scali, która zachowuje typ dynamiczny?

Oto funkcja mam tak daleko:

def subscript_get(tup: Product, index:Int): Any={ 
    return tup.productElement(index)  
} 

Wykorzystanie na przykład byłoby:

subscript_get((0,1,2,3),0) --> Int = 0

subscript_get((0,1,"asdf",3),2) --> java.lang.String = asdf

wiem, że mogę rzucić wynik z powrotem potem do tego, czego szukam, ale to nie działa dla mnie, ponieważ nie zawsze wiem, do jakiego typu powinienem oddać.

Czy coś takiego jest możliwe? Dzięki!

+0

Można to zrobić za pomocą makr w wersji 2.10, ale tylko w przypadku dosłownych argumentów "index" (i tylko z powodu tego (http://stackoverflow.com/q/13669974/334519) dziwnego "nieokreślonego, ale zamierzone "zachowanie". –

+0

Niezależnie od makr, nie ma sensu mówić o udostępnianiu statycznych informacji dynamicznych. –

+1

@TravisBrown dał ci dobrą odpowiedź na literalnie używane strony z telefonami. Czy możesz podać przykłady tego, co masz na myśli, aby nie literalnie używać wywołań dla 'subscript_get'? W szczególności, czy używasz 'produktu' w podpisie powyżej, ponieważ naprawdę chcesz, aby typ był odroczony do czasu wykonania lub używasz go, ponieważ chcesz streścić wszystkie możliwe typy produktów podczas kompilacji? –

Odpowiedz

0

Nie możesz tego zrobić. Jeśli użyjesz Product, tracisz typ (kompilacja) wartości w krotkach. Ponadto, metoda nie może dostosować swojego typu zwrotu w oparciu o wartość, którą przekazujesz (nie do końca prawdziwe, patrz dependent method types, ale prawdziwe dla Int).

Jeśli nie wiesz, jaki typ oddanych do, można użyć dopasowywania wzoru:

subscript_get(..., 1) match { 
    case v: Int => // do something with Int 
    case v: String => // do something with String 
    // snip 
    case _ => sys.error("don't know how to handle this") 
} 
11

nie jestem pewien, że chcesz rozwiązanie, które używa makra, ale do rekordu (a ponieważ” napisz dokładnie tę metodę wcześniej), oto jak możesz to zaimplementować w systemie makro w wersji 2.10.

Jak napisałem w komentarzu powyżej, podejście to wymaga, aby index było literałem całkowitym i opierało się na "underspecified but intended" behavior w 2.10. Podnosi także some tricky questions o dokumentacji.

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object ProductIndexer { 
    def at[T <: Product](t: T)(index: Int) = macro at_impl[T] 
    def at_impl[T <: Product: c.WeakTypeTag](c: Context) 
    (t: c.Expr[T])(index: c.Expr[Int]) = { 
    import c.universe._ 

    index.tree match { 
     case Literal(Constant(n: Int)) if 
     n >= 0 && 
     weakTypeOf[T].members.exists { 
      case m: MethodSymbol => m.name.decoded == "_" + (n + 1).toString 
      case _ => false 
     } => c.Expr[Any](Select(t.tree, newTermName("_" + (n + 1).toString))) 
     case Literal(Constant(_: Int)) => c.abort(
     c.enclosingPosition, 
     "There is no element at the specified index!" 
    ) 
     case _ => c.abort(
     c.enclosingPosition, 
     "You must provide an integer literal!" 
    ) 
    } 
    } 
} 

A potem:

scala> import ProductIndexer._ 
import ProductIndexer._ 

scala> val triple = (1, 'a, "a") 
triple: (Int, Symbol, String) = (1,'a,a) 

scala> at(triple)(0) 
res0: Int = 1 

scala> at(triple)(1) 
res1: Symbol = 'a 

scala> at(triple)(2) 
res2: String = a 

Wszystko statycznie wpisane zgodnie z oczekiwaniami, a jeśli dać to indeks, który znajduje się poza zasięgiem (lub nie dosłownym), można uzyskać ładne błąd kompilacji.

+0

To jest naprawdę miłe. Szkoda, że ​​wymaga literałów. Czy nie moglibyśmy użyć ukrytych dowodów na temat wartości indeksu? : P – gzm0

Powiązane problemy