4.4. kink/BINDING¶
The companion mod for binding vals.
Example:
:BINDING.require_from('kink/')
:PROGRAM.require_from('kink/')
:Binding <- BINDING.new
:prog <- PROGRAM.compile('Num * 2'){(:C)
C.binding(Binding)
}
Binding:Num <- 42
stdout.print_line(prog.repr) # => 84
4.4.1. type binding¶
A binding val is a holder of local variables.
See Language specification → Evaluation chapter about manipulation of bindings.
Every binding has preloaded funs such as `nada` and `new_val`.
4.4.1.1. nada¶
“Just this creaking metal sign that said nada, nada, against the blue sky. I used to wake up hollering” ―― The Crying of Lot 49 (1966, Thomas Pynchon).
`nada` returns nada val, which is identical to the result of the expression “()”.
stdout.print_line(nada.repr) # => nada
nada is used when no meaningful val is needed. For example, Varref.op_store returns nada val.
stdout.print_line((:X <- 49).repr) # => nada
4.4.1.2. true¶
`true` returns the true bool val.
4.4.1.3. false¶
`false` returns the false bool val.
4.4.1.4. new_val(...[Sym1 Val1 Sym2 Val2 ,,,])¶
`new_val` makes a new val, assigning vals to corresponding vars specified by the syms.
Preconditions:
• The number of args can be 0, 2, 4, or an arbitrary even number.
• `Sym1`, `Sym2` ,,, must be strs.
If the same sym is specified multiple times, the last val is assigned to the var.
Example:
:V <- new_val(
'bark' { 'Bow' }
'howl' { 'Wow' }
)
stdout.print_line(V.bark) # => Bow
stdout.print_line(V.howl) # => Wow
The program above is equivalent to the following one.
:V <- new_val
V:bark <- { 'Bow' }
V:howl <- { 'Wow' }
stdout.print_line(V.bark) # => Bow
stdout.print_line(V.howl) # => Wow
It is a idiom to store sym-val pairs as a vec, then spread it when calling new_val fun. Such an array is called a trait. Example:
# make a rational val
:new_rational <- {(:Numer :Denom)
new_val(
# reuse Rational_trait, which is common to all rational vals
... Rational_trait
# Data vars
'Numer' Numer
'Denom' Denom
)
}
# trait for rational vals
:Rational_trait <- [
'numer' {[:R]() R.Numer }
'denom' {[:R]() R.Denom }
'repr' {[:R]()
'{}/{}'.format(R.numer R.denom)
}
]
stdout.print_line(new_rational(2 3).repr) # => 2/3
stdout.print_line(new_rational(4 5).repr) # => 4/5
4.4.1.5. raise(Msg)¶
`raise` raises an exception with `Msg` as the message, and the current traces as the traces.
Precondition:
• `Msg` must be a str.
`raise` can be defined as follows.
:EXCEPTION.require_from('kink/')
:TRACE.require_from('kink/')
:raise <- {(:Msg)
:Exc = EXCEPTION.new(Msg TRACE.current_traces)
Exc.raise
}
4.4.1.6. op_lognot(Bool)¶
`op_lognot` returns logical negation of `Bool`.
Precondition:
• `Bool` must be a bool.
If `Bool` is true, `op_lognot` returns false.
If `Bool` is false, `op_lognot` returns true.
Note that expression “! X” is translated to “op_lognot(X)”. See Semantics section of the manual.
Example:
stdout.print_line((! true).repr) # => false
stdout.print_line((! false).repr) # => true
The code above is translated to the following:
stdout.print_line(op_lognot(true).repr) # => false
stdout.print_line(op_lognot(false).repr) # => true
4.4.1.7. op_logor(Bool $thunk)¶
`op_logor` returns logical OR of two bools.
Preconditions:
• `Bool` must be a bool
• $thunk must be a fun
If `Bool` is true, `op_logor` returns true.
If `Bool` is false, `op_logor` tail-calls $thunk.
Hence, $thunk is called only when `Bool` is false.
Note that expression “X || Y” is translated to “op_logor(X { Y })”. See Semantics section of the manual.
Example:
stdout.print_line((true || true)) => true
stdout.print_line((true || false)) => true
stdout.print_line((false || true)) => true
stdout.print_line((false || false)) => false
The code above is translated to the following:
stdout.print_line(op_logor(true { true })) => true
stdout.print_line(op_logor(true { false })) => true
stdout.print_line(op_logor(false { true })) => true
stdout.print_line(op_logor(false { false })) => false
4.4.1.8. op_logand(Bool $thunk)¶
`op_logand` returns logical AND of two bools.
Preconditions:
• `Bool` must be a bool
• $thunk must be a fun
If `Bool` is true, `op_logor` tail-calls $thunk.
If `Bool` is false, `op_logor` returns false.
Hence, $thunk is called only when `Bool` is true.
Note that expression “X && Y” is translated to “op_logand(X { Y })”. See Semantics section of the manual.
Example:
stdout.print_line((true && true)) => true
stdout.print_line((true && false)) => false
stdout.print_line((false && true)) => false
stdout.print_line((false && false)) => false
The code above is translated to the following:
stdout.print_line(op_logand(true { true })) => true
stdout.print_line(op_logand(true { false })) => false
stdout.print_line(op_logand(false { true })) => false
stdout.print_line(op_logand(false { false })) => false
4.4.1.9. if(Bool $true_cont ...[$false_cont={}])¶
`if` does 1-way or 2-way branch.
Preconditions:
• `Bool` must be a bool
• $true_cont must be a fun
• $false_cont, if given, must be a fun
If `Bool` is true, `if` tail-calls $true_cont.
If `Bool` is false, and $false_cont is given, `if` tail-calls $false_cont.
If `Bool` is false, and $false_cont is not given, `if` returns nada.
Example: 1 way branch
:print_if_even <- {(:N)
if(N % 2 == 0){
stdout.print_line('even number {}'.format(N.repr))
}
}
print_if_even(8) # => even number 8
print_if_even(11) # => (no output)
Example: 2 way branch
:compare <- {(Num)
if(Num < 100
{ 'less than 100' }
{ 'greater than or equal to 100' }
)
}
stdout.print_line(compare(50)) # => less than 100
stdout.print_line(compare(200)) # => greater than or equal to 100
4.4.1.10. branch(...[$cond1 $then1 $cond2 $then2 ,,,])¶
`branch` does a multiway branch.
First, `branch` calls $cond1, $cond2, ,,, in the order of occurrence with no args, till one of the cond thunks returns true. If no cond thunk returns true, `branch` raises an exception.
Second, `branch` tail-calls the `then` thunk corresponding to the `cond` thunk which returned true.
The number of args can be 0, 2, 4, or an arbitrary even number.
$cond1, $cond2, ,,, must take no args, and return true or false.
$then1, $then2, ,,, must take no args.
Example:
:Sorted_nums <- [1 2 3 5 8 11 19 30 49 89]
# returns whether Sorted_nums contains N, by binary search algorithm
:contain? <- {(:N)
# `if` and `branch` tail-calls a body thunk.
# Thus the loop does not cause a stack overflow
# even when Sorted_nums contains a large number of elements.
:in_range? <- {(:From :To)
if(From < To
{ :Ind = (From + To) // 2
:T = Sorted_nums.get(Ind)
branch(
{ T < N } { in_range?(Ind + 1 To) }
{ T > N } { in_range?(From Ind) }
$true { true }
)
}
{ false }
)
}
in_range?(0 Sorted_nums.size)
}
stdout.print_line(contain?(30).repr) # => true
stdout.print_line(contain?(42).repr) # => false
If you do a one-way or two-way branch, `if` might be a better choice.
4.4.1.11. stdin¶
`stdin` returns a scanner+input for the standard input of the runtime system.
When the stdin scanner+input is used as a scanner, it decodes the byte stream using the runtime default encoding (kink/RUNTIME.encoding_name).
Example: read all from the stdin then dump it to the stdout
:INPUT.require_from('kink/io/')
:Bin <- INPUT.read_all(stdin)
stdout.write(Bin)
Example: emulates "cat -n"
:loop <- {(:N)
stdin.scan_line.with_just_or(
{(:Line)
stdout.print("{}\t{}".format(N Line))
loop(N + 1)
}
{}
)
}
loop(1)
Example: scan lines from the stdin decoding the byte stream as UTF-8
:WRAP_SCANNER.require_from('kink/io/')
:CHARSET.require_from('kink/charset/')
:Scanner <- WRAP_SCANNER.new(stdin CHARSET.utf8)
:loop <- {(:N)
Scanner.scan_line.with_just_or(
{(:Line)
stdout.print("{}\t{}".format(N Line))
loop(N + 1)
}
{}
)
}
loop(1)
4.4.1.12. stdout¶
`stdout` returns a printer+output for the standard output of the runtime system.
When the stdout printer+output is used as a printer, it terminates lines using the runtime default newline str (kink/STR.default_newline), and encodes the str using the runtime default charset (kink/charset/CHARSET.default).
Example: writes bin to the stdout
:BIN.require_from('kink/')
stdout.write(BIN.of(0x66 0x6f 0x6f 0x62 0x61 0x72 0x0a)) # => foobar
Example: prints a line to the stdout
stdout.print_line('foobar') # => foobar
Example: prints a line using UTF-8 and LF
:WRAP_PRINTER.require_from('kink/io/')
:CHARSET.require_from('kink/charset/')
:Printer <- WRAP_PRINTER.new(stdout CHARSET.utf8 "\n")
Printer.print_line('foobar') # => foobar
4.4.1.13. stderr¶
`stderr` returns a printer+output for the standard error output of the runtime system.
When the stderr printer+output is used as a printer, it terminates lines using the runtime default newline str (kink/STR.default_newline), and encodes the str using the runtime default charset (kink/charset/CHARSET.default).
4.4.1.14. Binding.repr¶
`repr` returns a str such as "(binding val_id=42)".
4.4.2. BINDING.new¶
`new` makes a new binding.
4.4.3. BINDING.is?(Val)¶
`is?` returns whether the `Val` is a binding val.