lyng/docs/advanced_topics.md

161 lines
4.2 KiB
Markdown

# Advanced topics
__See also:__ [parallelism].
## Closures/scopes isolation
Each block has own scope, in which it can safely use closures and override
outer vars. Lets use some lambdas to create isolated scopes:
var param = "global"
val prefix = "param in "
val scope1 = {
var param = prefix + "scope1"
param
}
val scope2 = {
var param = prefix + "scope2"
param
}
println(scope1())
println(scope2())
println(param)
>>> param in scope1
>>> param in scope2
>>> global
>>> void
One interesting way of using closure isolation is to keep state of the functions:
val getAndIncrement = {
// will be updated by doIt()
var counter = 0
// we return callable fn from the block:
fun doit() {
val was = counter
counter = counter + 1
was
}
}()
// notice using of () above: it calls the lambda block that returns
// a function (callable!) that we will use:
println(getAndIncrement())
println(getAndIncrement())
println(getAndIncrement())
>>> 0
>>> 1
>>> 2
>>> void
Inner `counter` is not accessible from outside, no way; still it is kept
between calls in the closure, as inner function `doit`, returned from the
block, keeps reference to it and keeps it alive.
The example above could be rewritten using inner lambda, too:
val getAndIncrement = {
// will be updated by doIt()
var counter = 0
// we return callable fn from the block:
{
val was = counter
counter = counter + 1
was
}
}()
// notice using of () above: it calls the lambda block that returns
// a function (callable!) that we will use:
println(getAndIncrement())
println(getAndIncrement())
println(getAndIncrement())
>>> 0
>>> 1
>>> 2
>>> void
Lambda functions remember their scopes, so it will work the same as previous:
var counter = 200
fun createLambda() {
var counter = 0
{ counter += 1 }
}
val c = createLambda()
println(c)
>> 1
>> void
# Elements of functional programming
With ellipsis and splats you can create partial functions, manipulate
arguments list in almost arbitrary ways. For example:
// Swap first and last arguments for call
fun swap_args(first, others..., last, f) {
f(last, ...others, first)
}
fun glue(args...) {
var result = ""
for( a in args ) result += a
}
assertEquals(
"4231",
swap_args( 1, 2, 3, 4, glue)
)
>>> void
# Annotations
Annotation in Lyng resembles these proposed for Javascript. Annotation is just regular functions that, if used as annotation, are called when defining a function, var, val or class.
## Function annotation
When used without params, annotation calls a function with two arguments: actual function name and callable function body. Function annotation __must return callable for the function__, either what it received as a second argument (most often), or something else. Annotation name convention is upper scaled:
var annotated = false
// this is annotation function:
fun Special(name, body) {
assertEquals("foo", name)
annotated = true
{ body(it) + 100 }
}
@Special
fun foo(value) { value + 1 }
assert(annotated)
assertEquals(111, foo( 10 ))
>>> void
Function annotation can have more args specified at call time. There arguments must follow two mandatory ones (name and body). Use default values in order to allow parameterless annotation to be used simultaneously.
val registered = Map()
// it is recommended to provide defaults for extra parameters:
fun Registered(name, body, overrideName = null) {
registered[ overrideName ?: name ] = body
body
}
// witout parameters is Ok as we provided default value
@Registered
fun foo() { "called foo" }
@Registered("bar")
fun foo2() { "called foo2" }
assertEquals(registered["foo"](), "called foo")
assertEquals(registered["bar"](), "called foo2")
>>> void
[parallelism]: parallelism.md