Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I pass an arbitrary function to another function in Scala?

I'm new to Scala, and being able to pass functions to other functions is pretty neat-- but can I pass an arbitrary function reference to another function? The arity of said functional parameter will be fixed (that said, I'm also curious about whether you can pass a function with arbitrary arity as well). I keep getting tripped up on type errors. I've tried using Any but it doesn't seem to help.

E.g., I have the code below:

class CodeRunner(val user_defined: (Int) => Unit) {
  def run(input: Int) = {
    user_defined(input)
  }
}

def arbitrary_code(input: Int) = { println("Running with input " + input) }

val d1 = new CodeRunner(arbitrary_code)

d1.run(4)

And I get:

Running with input 4

Now, let's say that I want to pass the following function instead:

def arbitrary_code(input: String) = { println("Running with input " + input) }

How can I change my CodeRunner class to handle both?

like image 996
Dan Barowy Avatar asked Jun 14 '11 19:06

Dan Barowy


People also ask

How do you pass a function as a parameter in Scala?

In Scala, we can pass a function as a parameter to another function or say method. For that we have to follow the below guidelines. Define your method , including the function signature that it will accepts as a parameter. Define the function with the exact signature that will be passed a method parameter.

Is it possible to use another function as parameter?

Because functions are objects we can pass them as arguments to other functions. Functions that can accept other functions as arguments are also called higher-order functions.

What is the meaning of => in Scala?

=> is syntactic sugar for creating instances of functions. Recall that every function in scala is an instance of a class. For example, the type Int => String , is equivalent to the type Function1[Int,String] i.e. a function that takes an argument of type Int and returns a String .

Is Scala pass by reference?

In Java and Scala, certain builtin (a/k/a primitive) types get passed-by-value (e.g. int or Int) automatically, and every user defined type is passed-by-reference (i.e. must manually copy them to pass only their value).


2 Answers

How can I change my CodeRunner class to handle both?

You can make the arbitrary type a parameter of the class:

class CodeRunner[T](val user_defined: (T) => Unit) {
  def run(input: T) = {
    user_defined(input)
  }
}

def arbitrary_code(input: Int) = { println("Running with input " + input) }

val d1 = new CodeRunner(arbitrary_code)

d1.run(4)

def arbitrary_code2(input: String) = { println("Running with input " + input) }

val d2 = new CodeRunner(arbitrary_code2)

d2.run("hello")

Note that the type of d2 is CodeRunner[String] which is not assignable to d1 which is CodeRunner[Int].

like image 193
Ben Jackson Avatar answered Sep 30 '22 02:09

Ben Jackson


Generic types allow you to define a class with a placeholder type that gets specified when an object gets instantiated. The compiler is happy because it can make sure that everything is type safe, and you're happy because you can instantiate the object and pass in arbitrary types for the value.

To use a generic type with your class, you could modify it like this:

class CodeRunner[T] (val user_defined: (T) => Unit) {
  def run(input: T) = {
    user_defined(input)
  }
}

The [T] after "class CodeRunner" is the important part -- it defines that there is a generic type T (you could replace T with another capital letter, etc.) which will be used within the class definition.

So, if you define a method:

def arbitrary_code(input: String) = { println("Running with input " + input) }

and then pass it in:

val d1 = new CodeRunner(arbitrary_code)

... the compiler then says "aha, for this instance of CodeRunner the generic type T is a string". And if you invoke

d1.run("string")

the compiler will be happy, but won't let you pass in d1.run(4).

like image 21
Josh Marcus Avatar answered Sep 30 '22 02:09

Josh Marcus