Scala function conversion

Say I have a function that takes one argument

def fun(x: Int) = x 

Based on this, I want to generate a new function with the same call, but this will apply some conversion to its arguments before passing the original function. For this I could

 def wrap_fun(f: (Int) => Int) = (x: Int) => f(x * 2) wrap_fun(fun)(2) // 4 

How can one do the same thing, except for functions of any arity that have only a part of the arguments in order to apply the transformation to the general?

 def fun1(x: Int, y: Int) = x def fun2(x: Int, foo: Map[Int,Str], bar: Seq[Seq[Int]]) = x wrap_fun(fun1)(2, 4) // 4 wrap_fun(fun2)(2, Map(), Seq()) // 4 

What would a wrap_fun definition look like to create work on calls?

+6
source share
4 answers

This can be done quite simply by using formless ones to abstract on the arity function,

 import shapeless._ import HList._ import Functions._ def wrap_fun[F, T <: HList, R](f : F) (implicit hl : FnHListerAux[F, (Int :: T) => R], unhl : FnUnHListerAux[(Int :: T) => R, F]) = ((x : Int :: T) => f.hlisted(x.head*2 :: x.tail)).unhlisted val f1 = wrap_fun(fun _) val f2 = wrap_fun(fun1 _) val f3 = wrap_fun(fun2 _) 

Sample REPL Session,

 scala> f1(2) res0: Int = 4 scala> f2(2, 4) res1: Int = 4 scala> f3(2, Map(), Seq()) res2: Int = 4 

Note that you cannot apply the wrapped function immediately (as in the question), and not through the assigned val (as I did above), because the explicit list of arguments of the wrapped function will confuse the wrap_fun list with the implicit argument. The closest we can get to the form in the question is to explicitly specify the apply method, as shown below,

 scala> wrap_fun(fun _).apply(2) res3: Int = 4 scala> wrap_fun(fun1 _).apply(2, 4) res4: Int = 4 scala> wrap_fun(fun2 _).apply(2, Map(), Seq()) res5: Int = 4 

Here, the explicit mention of apply syntactically marks the first application (from wrap_fun along with its implicit list of arguments) from the second application (the transformed function with its explicit list of arguments).

+6
source

As usual in Scala, there is another way to achieve what you want to do.

Here is an example based on currying the first argument along with compose of Function1 :

 def fun1(x : Int)(y : Int) = x def fun2(x : Int)(foo : Map[Int, String], bar : Seq[Seq[Int]]) = x def modify(x : Int) = 2*x 

Result types like REPL indicate that you will:

 fun1: (x: Int)(y: Int)Int fun2: (x: Int)(foo: Map[Int,String], bar: Seq[Seq[Int]])Int modify: (x: Int)Int 

And instead of wrapping the functions fun1 and fun2 , you compose them, as technically, they are now objects of Function1 . This allows you to make calls such as:

 (fun1 _ compose modify)(2)(5) (fun2 _ compose modify)(2)(Map(), Seq()) 

Both of them will return 4. Of course, the syntax is not so good, considering that you need to add _ to distinguish the fun1 application from the function object itself (on which you want to call compose in this case).

So, Luigi’s argument that this is impossible at all remains valid, but if you are free to perform your functions, you can do it in such a good way.

+6
source

Since functions that take different numbers of arguments are different, unrelated types, you cannot do this in the general case. trait Function1 [-T1, +R] extends AnyRef and nothing more. You will need a separate method for each purpose.

+2
source

So far I have voted and agreed with Luigi, because, you know ... he is right; Scala does not have direct built-in support for such a thing - it is worth noting that what you are trying to do is not impossible; it’s just that it’s a bit of a pain to leave, and often times, it’s best for you to simply implement a separate method for each desired goal.

However, though ... we can actually do this HList s. If you are interested in trying, naturally, you need to get an implementation of HList . I recommend using the excellent Miles Sabin shapeless project and its implementation of HList s. Anyway, here is an example of its use that does something similar to what you seem to be looking for:

 import shapeless._ trait WrapperFunner[T] { type Inputs <: HList def wrapFun(inputs: Inputs) : T } class WrapsOne extends WrapperFunner[Int] { type Inputs = Int :: HNil def wrapFun(inputs: Inputs) : Int = { inputs match { case num :: HNil => num * 2 } } } class WrapsThree extends WrapperFunner[String] { type Inputs = Int :: Int :: String :: HNil def wrapFun(inputs: Inputs) : String = { inputs match { case firstNum :: secondNum :: str :: HNil => str + (firstNum - secondNum) } } } object MyApp extends App { val wo = new WrapsOne println(wo.wrapFun(1 :: HNil)) println(wo.wrapFun(17 :: HNil)) //println(wo.wrapFun(18 :: 13 :: HNil)) // Would give type error val wt = new WrapsThree println(wt.wrapFun(5 :: 1 :: "your result is: " :: HNil)) val (first, second) = (60, 50) println(wt.wrapFun(first :: second :: "%s minus %s is: ".format(first, second) :: HNil)) //println(wt.wrapFun(1 :: HNil)) // Would give type error } 

Running MyApp results in:

 2 34 your result is: 4 60 minus 50 is: 10 

Or, extended closer to your specific case:

 import shapeless._ trait WrapperFunner[T] { type Inputs <: HList def wrapFun(inputs: Inputs) : T } trait WrapperFunnerBase extends WrapperFunner[Int] { // Does not override `Inputs` def wrapFun(inputs: Inputs) : Int = { inputs match { case (num: Int) :: remainder => num } } } class IgnoresNothing extends WrapperFunnerBase { type Inputs = Int :: HNil } class IgnoresLastTwo extends WrapperFunnerBase { type Inputs = Int :: Int :: String :: HNil } object MyApp extends App { val in = new IgnoresNothing println(in.wrapFun(1 :: HNil)) println(in.wrapFun(2 :: HNil)) //println(in.wrapFun(3 :: 4 :: HNil)) // Would give type error val ilt = new IgnoresLastTwo println(ilt.wrapFun(60 :: 13 :: "stupid string" :: HNil)) println(ilt.wrapFun(43 :: 7 :: "man, that string was stupid..." :: HNil)) //println(ilt.wrapFun(1 :: HNil)) // Would give type error } 

leads to:

 1 2 60 43 
+1
source

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


All Articles