2013-10-10 11 views
17

Sto tentando di imparare Shapeless (utilizzando la versione 2.10.2). Ho creato un semplice record di estendibile:Trasferimento di un record estendibile senza forma a una funzione

val rec1 = ("foo" ->> 42) :: HNil

Secondo il REPL, questo è di tipo

shapeless.::[Int with shapeless.record.KeyTag[String("foo"),Int],shapeless.HNil]

Sto cercando di definire una semplice funzione:

def fun(x: ::[Int with KeyTag[String("foo"), Int], HNil]) = x("foo") 

ma non viene nemmeno compilato. Non posso usare una stringa ("pippo") nella dichiarazione del tipo e ottenere un errore.

Ho due domande:

  1. Come posso specificare il tipo di record estendibile nel mio codice?
  2. Quando si lavora con record con più campi, la lunghezza e la complessità della dichiarazione del tipo saranno ingestibili. C'è un modo per creare un alias per il tipo, data una particolare istanza di un record, o qualche altra soluzione alternativa?

EDIT

ho scoperto che:

val rec1 = ("foo" ->> 42) :: HNil 
val rec2 = ("foo" ->> 43) :: HNil 
var x = rec1 
x = rec2 

funziona bene. Concludo rec1, rec2 e x sono dello stesso tipo. Solo non so come esprimere quel tipo di codice!

risposta

24

Ecco qualcosa di più generale che penso possa rispondere alla tua domanda. Supponiamo di voler scrivere un metodo che funzioni su qualsiasi record con una chiave "foo". Siamo in grado di utilizzare una combinazione di un testimone e un selettore:

import shapeless._, record._, syntax.singleton._ 

val w = Witness("foo") 

def fun[L <: HList](xs: L)(implicit sel: ops.record.Selector[L, w.T]) = xs("foo") 

E poi:

scala> fun(("foo" ->> 42) :: HNil) 
res0: Int = 42 

Oppure:

scala> fun(("bar" ->> 'a) :: ("foo" ->> 42) :: HNil) 
res1: Int = 42 

Se volevamo consentire solo i record con nessun altro campo , potremmo scrivere quanto segue:

def fun(l: Int with KeyTag[w.T, Int] :: HNil) = l("foo") 

Ma ciò è in qualche modo in disaccordo con il modo in cui i record vengono generalmente utilizzati.

Dobbiamo definire il testimone proprio perché Scala 2.10 non fornisce alcun modo per fare riferimento a un tipo di singleton direttamente-vedere ad esempio my fork del progetto di Alois Cochard Shona per qualche discussione.

Aggiungerò come dichiarazione di non responsabilità finale che sto solo ora familiarizzando con Shapeless 2.0 da solo, ma non penso che nemmeno Miles sia abbastanza magico da aggirare questa limitazione.

+0

Per la cronaca, Miles ha appena detto [su Twitter] (https://twitter.com/milessabin/status/388623399624646656) che potrebbe essere in grado di "rimuovere parte del disordine sintattico prima che la 2.0.0 diventi definitiva". –

+1

Se abbiamo molte funzioni come divertimento, dobbiamo ripetere il parametro implicito per ciascuna. È doloroso e errorprone se tali funzioni devono accedere a più di un campo (ad es. Foo, bar, ...). C'è un modo per ridimensionare le dichiarazioni dei selettori? – bhericher

5

A partire da informe 2.1.0 c'è un new syntax di esprimere tipi di record:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

import shapeless._ 
import shapeless.record._ 
import shapeless.syntax.singleton._ 

def fun(x: Record.`"foo" -> Int`.T) = x("foo") 

// Exiting paste mode, now interpreting. 

import shapeless._ 
import shapeless.record._ 
import shapeless.syntax.singleton._ 
fun: (x: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil])Int 

scala> fun(("foo" ->> 42) :: HNil) 
res2: Int = 42 

scala> fun(("foo" ->> 42) :: ("bar" ->> 43) :: HNil) 
<console>:30: error: type mismatch; 
found : shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.::[Int with shapeless.labelled.KeyTag[String("bar"),Int],shapeless.HNil]] 
required: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil] 
     fun(("foo" ->> 42) :: ("bar" ->> 43) :: HNil) 

Ma il selettore è probabilmente l'approccio migliore per l'uso a caso di OP.

Problemi correlati