Reputation: 4510
I have not understood the following code snippet why afterDelay(0) {...}
, a locally defined function can be stored into agenda? Can someone help me understand the afterDelay(0) {...}
in the run
function?
abstract class Simulation {
type Action = () => Unit
case class WorkItem(time: Int, action: Action)
private var curtime = 0
def currentTime: Int = curtime
private var agenda: List[WorkItem] = List()
private def insert(ag: List[WorkItem], item: WorkItem): List[WorkItem] = {
if (ag.isEmpty || item.time < ag.head.time) item :: ag
else ag.head :: insert(ag.tail, item)
}
def afterDelay(delay: Int)(block: => Unit) {
val item = WorkItem(currentTime + delay, () => block)
agenda = insert(agenda, item)
}
private def next() {
(agenda: @unchecked) match {
case item :: rest =>
agenda = rest
curtime = item.time
item.action()
}
}
def run() {
afterDelay(0) {
println("*** simulation started, time = "+
currentTime +" ***")
}
while (!agenda.isEmpty) next()
}
}
Upvotes: 2
Views: 218
Reputation: 3608
You defined afterDelay as a curried function. This means it has two parameter lists. In scala you can replace the brackets surounding the parameterlist (...)
with {...}
. In the second parameterlist you are using a "call-by-name" parameter. Those parameters are evaluated everytime again you use them in your function. A good Example is here.
"Call-by-name" parameters are often used to define your own controlling structure.
def do(until: Int)(body: => Unit) {
body
if (until > 1) do(until - 1)(body)
}
do(0)(println("test"))
do(5)(println("test2"))
This is an example for a do until. It will print once test
and five times test2
.
Upvotes: 1
Reputation:
afterDelay(0) {
println(...)
}
Is equivalent to the following:
afterDelay(0)({
println(...)
})
The function afterDelay
is invoked a new WorkItem
(item
) is added to the list, not the function itself. The parameter block: => Unit
is a "By-Name Parameter" (see the Scala Language Specification section 4.6.1): the expression used as the argument is implicitly converted into a "parameterless method" (without being evaluated first) that will be invoked whenever the variable inside the method is accessed (no ()
required).
In this case that is when the function resulting from () => block
is invoked: it is invoked at item.action()
which occurs at some point after the new WorkItem
is added to the list (and afterDelay
returns).
If it was written as (taking in a function paramater, not a by-name/thunk):
def afterDelay(delay: Int)(block: () => Unit) { // take a function
// new function will invoke function named by "block" when invoked ...
val item = WorkItem(..., () => block())
// or skip wrapping the function in a function ...
// val item = WorkItem(..., block)
...
}
Then it would need to be invoked passing in a function:
afterDelay(0)(() => { // need a function
println(...)
})
Or, alternative syntax, still a function of () => Unit
, but the outside parenthesis can be avoided:
afterDelay(0) { () => // need a function
println(...)
}
Extract from the SLS, 4.6.1 By-Name Parameters:
The type of a value parameter may be prefixed by
=>
, e.g.x: => T
. The type of such a parameter is then the parameterless method type=> T
. This indicates that the corresponding argument is not evaluated at the point of function application, but instead is evaluated at each use within the function. That is, the argument is evaluated using call-by-name.
Upvotes: 3