2013-06-19 12 views
6

Desidero definire una funzione f che riprenda un'altra funzione g. Richiediamo g per prendere n Doppio (per alcuni fisso n) e restituire un doppio. La funzione chiamata f(g) dovrebbe restituire il valore specifico di n.Funzione scala generica il cui input è una funzione di arity variabile

Ad esempio, poiché f(Math.max) = 2 Math.sin ha tipo (Double, Double) => Double e f(Math.sin) = 1 poiché Math.sin è di tipo Double => Double.

Come definire f utilizzando i generici Scala?

Ho provato diverse forme senza successo. Per esempio:

def f[A <: Product](g: Product => Double) = {...} 

Questo non funziona perché non siamo in grado di estrarre il valore di n in fase di compilazione, e non può vincolare la A a contenere solo Double valori.

+0

Si dice che 'g' accetta una n-tupla, ma il tuo esempio' Math.max' è una funzione n-ario, non una 'Funzione1' che accetta una tupla. Dovresti chiarirlo. –

+0

Risolto, buona cattura – tba

risposta

2

Probabilmente la soluzione più semplice è quella di utilizzare un sovraccarico come

def f(g:() => Double) = 0; 
def f(g: (Double) => Double) = 1; 
def f(g: (Double, Double) => Double) = 2; 
def f(g: (Double, Double, Double) => Double) = 2; 
// ... 

println(f(Math.pow _)); 
println(f(Math.sin _)); 

(Non è possibile controllare argomento di funzione/tornare tipi in fase di esecuzione a causa di digitare la cancellazione, quindi credo che si possa 't creare una funzione completamente generica che soddisfi le vostre esigenze.)

4

Questa è stata una buona scusa per me di guardare in Shapeless, qualcosa che ho sempre voluto fare ad un certo punto :)

$ git clone [email protected]:milessabin/shapeless.git 
... 
$ cd shapeless 

(1)

Shapeless fornisce alcune astrazioni sull'arità e, in particolare, la rappresentazione come elenco eterogeneo (HList). Una funzione di arbitrarietà può essere vista come FnHList (una funzione che accetta un argomento come HList).

$ sbt shapeless-core/console 
scala> import shapeless._ 
import shapeless._ 

scala> def isFunction[A](fun: A)(implicit fnh: FnHLister[A]) {} 
isFunction: [A](fun: A)(implicit fnh: shapeless.FnHLister[A])Unit 

scala> isFunction(math.sqrt _) 

scala> isFunction(math.random _) 

(2)

Ora diamo richiedono che la funzione restituisce un Double:

scala> def isFunReturningDouble[A](fun: A)(implicit fnh: FnHLister[A] { type Result = Double }) {} 
isFunReturningDouble: [A](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double})Unit 

scala> isFunReturningDouble(math.sqrt _) 

scala> isFunReturningDouble(math.signum _) 
<console>:12: error: could not find implicit value for parameter fnh: shapeless.FnHLister[Int => Int]{type Result = Double} 
       isFunReturningDouble(math.signum _) 
           ^

(3)

La classe LUBConstraint tipo può testimoniare il limite superiore dell'argomento lista:

scala> def isValidFun[A, B <: HList](fun: A)(implicit fnh: FnHLister[A] { type Result = Double; type Args = B }, lub: LUBConstraint[B, Double]) {} 
isValidFun: [A, B <: shapeless.HList](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double; type Args = B}, implicit lub: shapeless.LUBConstraint[B,Double])Unit 

scala> isValidFun(math.random _) 

scala> isValidFun((i: Int) => i.toDouble) 
<console>:12: error: could not find implicit value for parameter lub: shapeless.LUBConstraint[B,Double] 
       isValidFun((i: Int) => i.toDouble) 
         ^

(4)

Ora abbiamo ancora bisogno di estrarre l'arity in qualche modo. A livello di tipo questo sarebbe Length fornito per HList. Per ottenere un valore di runtime, è necessaria un'altra classe di tipo ToInt.

Ecco la funzione finale:

import shapeless._ 

def doubleFunArity[A, B <: HList, C <: Nat](fun: A)(implicit 
    fnh: FnHLister[A] { type Result = Double; type Args = B }, 
    lub: LUBConstraint[B, Double], 
    len: Length[B] { type Out = C }, 
    res: ToInt[C] 
): Int = res() 

prova:

scala> doubleFunArity(math.sqrt _) 
res15: Int = 1 

scala> doubleFunArity(math.random _) 
res16: Int = 0 

scala> val g: (Double, Double) => Double = math.max _ 
g: (Double, Double) => Double = <function2> 

scala> doubleFunArity(g) 
res17: Int = 2 

Nota che purtroppo molti math operazioni sono sovraccarichi, e senza una forte vincolo di tipo, Scala non vi darà la Double versione automaticamente, ma utilizzerà la versione Int per qualche motivo:

scala> math.max _ 
res18: (Int, Int) => Int = <function2> 

Quindi ho bisogno dell'indirizzamento indiretto math.max _: ((Double, Double) => Double) per farlo funzionare.


Non dicendo che questo è il modo migliore per farlo nel tuo caso concreto, ma penso che sia stata un'esplorazione divertente.

+0

P.S. anche 'math.max (_: Double, _: Double)' –

Problemi correlati