2011-07-26 11 views
5

Esiste un modo rapido per utilizzare come funzione concreta (di tipo, ad esempio, (A) => B) come PartialFunction[A, B]? La sintassi più concisa che conosco è:Scala PartialFunctions from concrete

(a: A) => a match { case obj => func(obj) } 

Esiste una conversione implicita da nessuna parte, qualcosa di simile:

implicit def funcAsPartial[A, B](func: A => B) = new PartialFunction[A, B] { 

    def isDefinedAt(a: A) = true 
    def apply(a: A) = func(a) 

} 

Credo che ho appena scritto quello che stavo cercando, ma fa questo già esiste in le librerie Scala?

risposta

5

Fare questo con una conversione implicita è pericoloso, per lo stesso motivo che (A) => B non deve ereditare da PartialFunction[A, B]. Cioè, il contratto di PartialFunction garantisce che puoi tranquillamente * chiamare apply ovunque isDefinedAt restituisce true. Il contratto di Function1 non offre tale garanzia.

La conversione implicita genererà una funzione Partial che viola il contratto se la si applica a una funzione che non è definita ovunque. Al contrario, utilizzare un magnaccia per fare la conversione esplicita:

implicit def funcAsPartial[A, B](f: A => B) = new { 
    /** only use if `f` is defined everywhere */ 
    def asPartial(): PartialFunction[A, B] = { 
     case a => f(a) 
    } 

    def asPartial(isDefinedAt: A => Boolean): PartialFunction[A, B] = { 
     case a if isDefinedAt(a) => f(a) 
    } 
} 

// now you can write 
val f = (i: Int) => i * i 

val p = f.asPartial // defined on all integers 
val p2 = f.asPartial(_ > 0) // defined only on positive integers 

* Come discusso nei commenti, che potrebbero non essere del tutto chiaro che cosa "sicurezza": qui. Il mio modo di pensare è che una funzione Partial dichiara esplicitamente il proprio dominio nel seguente senso preciso: se isDefinedAt restituisce true per un valore x, allora apply(x) può essere valutato in un modo coerente con l'intento dell'autore della funzione. Che non implichi che apply(x) non genererà un'eccezione, ma semplicemente che l'eccezione faceva parte del progetto della funzione (e dovrebbe essere documentata).

+0

Grazie; Avevo l'idea sbagliata che Function1 implicava essere definita sull'intero dominio. –

+0

@AaronNovstrup: la spiegazione del contratto di PartialFunction è l'unica che abbia senso, ma non viene riflessa da ScalaDocs (almeno fino al 2.9.1). I programmi Scala di 'PartialFunction' affermano che:" Una funzione parziale di tipo 'PartialFunction [A, B]' è una funzione unaria in cui il dominio non include necessariamente tutti i valori di tipo 'A'." Inoltre, non affermano mai che è sicuro (in che senso) chiamare f ovunque definito, ed è piuttosto facile da violare, come fatto dal letterale '{case 0 => 1/0}' PartialFunction'. Dove hai preso quelle informazioni? È necessario presentare una segnalazione di bug? – Blaisorblade

+0

@Blaisorblade Credo di aver letto questa spiegazione sulla mailing list mentre stavo imparando Scala (è passato un po 'di tempo), e non stavo guardando alcuna documentazione quando ho scritto questa risposta. E, sì, è facile e anche un po 'comune violare questo contratto (ad esempio, per avvolgere/rilanciare un'eccezione in un blocco catch). Il punto reale è che le funzioni parziali definiscono il loro dominio mentre le funzioni ordinarie non lo fanno (con un po 'di confusione su ciò che in realtà significa). –

0

No, ho provato a trovarne uno qualche mese fa e ho finito per scrivere il mio che è essenzialmente uguale al tuo.

+0

Mi sembra che '(A) => B' debba ereditare da' PartialFunction [A, B] ', non viceversa. –

+1

Sarei d'accordo, sulla base del fatto che una funzione (totale) è una funzione parziale che si verifica ovunque (isDefinedAt (x) = true). Tuttavia, Martin Odersky afferma che una funzione non è garantita come una funzione totale, è solo che il suo dominio non è documentato. Quindi una funzione parziale è una funzione che documenta il suo dominio. –

+0

http://www.scala-lang.org/node/2750 –