A generic scala function whose input is a function of the arity variable

I want to define a function f that takes another function g . We need g to take take n Doubles (for some fixed n ) and return Double. A function call f(g) should return a specific value n .

For example, f(Math.max) = 2 , since Math.sin is of type (Double, Double) => Double and f(Math.sin) = 1 , since Math.sin is of type Double => Double .

How to define f using Scala generics?

I tried several forms without success. For instance:

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

This does not work, because we cannot extract the value of n at compile time and cannot restrict A just Double values.

+6
source share
3 answers

There is a template called Magnet Pattern created by the Spray team. He really does what you want.

+6
source

This was a good excuse for me to look into Shapeless , which I always wanted to do at some point :)

 $ git clone git@github.com :milessabin/shapeless.git ... $ cd shapeless 

(1)

Shapeless provides some abstractions over arity, and especially a heterogeneous list HList ( HList ). An arbitrary arity function can be thought of as an FnHList (a function that takes an HList argument).

 $ 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)

Now let me require the function to return a 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)

A class of type LUBConstraint can attest to the top of the argument list:

 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)

Now we still need to somehow extract the attribute. At the type level, this will be the Length that is provided for the HList . To get the runtime value, a different type of ToInt class is ToInt .

Here is the final function:

 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() 

Test:

 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 

Please note that, unfortunately, many math operations are overloaded and without a strong restriction like Scala will not give you the Double version automatically, but for some reason will use the Int version:

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

So I need to touch math.max _: ((Double, Double) => Double) to make this work.


Not to say that this is the best way to do this in your particular case, but I think it was a fun study.

+4
source

Probably the easiest solution is to use overload as

 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 _)); 

(You cannot check the types of arguments / return functions at runtime due to type erasure, so I believe that you cannot create a completely universal function that would suit your requirements.)

+2
source

Source: https://habr.com/ru/post/947630/


All Articles