What's the difference between => , ()=>, and Unit=>

Scala

Scala Problem Overview


I'm trying to represent a function that takes no arguments and returns no value (I'm simulating the setTimeout function in JavaScript, if you must know.)

case class Scheduled(time : Int, callback :  => Unit)

doesn't compile, saying " `val' parameters may not be call-by-name"

case class Scheduled(time : Int, callback :  () => Unit)  

compiles, but has to be invoked strangely, instead of

Scheduled(40, { println("x") } )

I have to do this

Scheduled(40, { () => println("x") } )      

What also works is

class Scheduled(time : Int, callback :  Unit => Unit)

but is invoked in an even-less-sensible way

 Scheduled(40, { x : Unit => println("x") } )

(What would a variable of type Unit be?) What I want of course is a constructor that can be invoke the way I would invoke it if it were an ordinary function:

 Scheduled(40, println("x") )

Give baby his bottle!

Scala Solutions


Solution 1 - Scala

Call-by-Name: => Type

The => Type notation stands for call-by-name, which is one of the many ways parameters can be passed. If you aren't familiar with them, I recommend taking some time to read that wikipedia article, even though nowadays it is mostly call-by-value and call-by-reference.

What it means is that what is passed is substituted for the value name inside the function. For example, take this function:

def f(x: => Int) = x * x

If I call it like this

var y = 0
f { y += 1; y }

Then the code will execute like this

{ y += 1; y } * { y += 1; y }

Though that raises the point of what happens if there's a identifier name clash. In traditional call-by-name, a mechanism called capture-avoiding substitution takes place to avoid name clashes. In Scala, however, this is implemented in another way with the same result -- identifier names inside the parameter can't refer to or shadow identifiers in the called function.

There are some other points related to call-by-name that I'll speak of after explaining the other two.

0-arity Functions: () => Type

The syntax () => Type stands for the type of a Function0. That is, a function which takes no parameters and returns something. This is equivalent to, say, calling the method size() -- it takes no parameters and returns a number.

It is interesting, however, that this syntax is very similar to the syntax for a anonymous function literal, which is the cause for some confusion. For example,

() => println("I'm an anonymous function")

is an anonymous function literal of arity 0, whose type is

() => Unit

So we could write:

val f: () => Unit = () => println("I'm an anonymous function")

It is important not to confuse the type with the value, however.

Unit => Type

This is actually just a Function1, whose first parameter is of type Unit. Other ways to write it would be (Unit) => Type or Function1[Unit, Type]. The thing is... this is unlikely to ever be what one wants. The Unit type's main purpose is indicating a value one is not interested in, so doesn't make sense to receive that value.

Consider, for instance,

def f(x: Unit) = ...

What could one possibly do with x? It can only have a single value, so one need not receive it. One possible use would be chaining functions returning Unit:

val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g

Because andThen is only defined on Function1, and the functions we are chaining are returning Unit, we had to define them as being of type Function1[Unit, Unit] to be able to chain them.

Sources of Confusion

The first source of confusion is thinking the similarity between type and literal that exists for 0-arity functions also exists for call-by-name. In other words, thinking that, because

() => { println("Hi!") }

is a literal for () => Unit, then

{ println("Hi!") }

would be a literal for => Unit. It is not. That is a block of code, not a literal.

Another source of confusion is that Unit type's value is written (), which looks like a 0-arity parameter list (but it is not).

Solution 2 - Scala

case class Scheduled(time : Int, callback :  => Unit)

The case modifier makes implicit val out of each argument to the constructor. Hence (as someone noted) if you remove case you can use a call-by-name parameter. The compiler could probably allow it anyway, but it might surprise people if it created val callback instead of morphing into lazy val callback.

When you change to callback: () => Unit now your case just takes a function rather than a call-by-name parameter. Obviously the function can be stored in val callback so there's no problem.

The easiest way to get what you want (Scheduled(40, println("x") ) where a call-by-name parameter is used to pass a lambda) is probably to skip the case and explicitly create the apply that you couldn't get in the first place:

class Scheduled(val time: Int, val callback: () => Unit) {
    def doit = callback()
}

object Scheduled {
    def apply(time: Int, callback: => Unit) =
        new Scheduled(time, { () => callback })
}

In use:

scala> Scheduled(1234, println("x"))
res0: Scheduled = Scheduled@5eb10190

scala> Scheduled(1234, println("x")).doit
x

Solution 3 - Scala

In the question, you want to simulate SetTimeOut function in JavaScript. Based on previous answers, I write following code:

class Scheduled(time: Int, cb: => Unit) {
  private def runCb = cb
}

object Scheduled {
  def apply(time: Int, cb: => Unit) = {
    val instance = new Scheduled(time, cb)
    Thread.sleep(time*1000)
    instance.runCb
  }
}

In REPL, we can get something like this:

scala> Scheduled(10, println("a")); Scheduled(1, println("b"))
a
b

Our simulation doesn't behave exactly the same as SetTimeOut, because our simulation is blocking function, but SetTimeOut is non-blocking.

Solution 4 - Scala

I do it this way (just don't want to break apply):

case class Thing[A](..., lazy: () => A) {}
object Thing {
  def of[A](..., a: => A): Thing[A] = Thing(..., () => a)
}

and call it

Thing.of(..., your_value)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMichael LortonView Question on Stackoverflow
Solution 1 - ScalaDaniel C. SobralView Answer on Stackoverflow
Solution 2 - ScalaBen JacksonView Answer on Stackoverflow
Solution 3 - ScalaJeff XuView Answer on Stackoverflow
Solution 4 - ScalaAlexey RykhalskiyView Answer on Stackoverflow