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?
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.
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.
=> 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 .
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).
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]
.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With