6.3. kink/BINDING¶
Operates `binding` values.
Example
:BINDING.require_from('kink/')
:PROGRAM.require_from('kink/program/')
:Binding <- BINDING.new
:Program <- PROGRAM.new('program' 'Num * 2')
:prog <- Program.compile{(:C)
C.binding(Binding)
}
Binding:Num <- 42
stdout.print_line(prog.repr) # => 84
6.3.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`.
6.3.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
6.3.1.2. true¶
`true` returns the true bool val.
6.3.1.3. false¶
`false` returns the false bool val.
6.3.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
6.3.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)
EXCEPTION.new(Msg).raise
}
6.3.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
6.3.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
6.3.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
6.3.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
6.3.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.
6.3.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 charset (CHARSET.default).
Example: read all from the stdin then dump it to the stdout
:Bin <- stdin.read_all
stdout.write(Bin)
Example: emulates "cat -n"
:loop <- {(:N)
stdin.scan_line{(:C)
C.on_success{(:Line)
stdout.print("{}\t{}".format(N Line))
loop(N + 1)
}
C.on_eof{}
}
}
loop(1)
Example: scan lines from the stdin decoding the byte stream as UTF-8
:INPUT_SCANNER.require_from('kink/io/')
:CHARSET.require_from('kink/')
:Scanner <- INPUT_SCANNER.new(stdin CHARSET.utf8)
:loop <- {(:N)
Scanner.scan_line{(:C)
C.on_success{(:Line)
stdout.print("{}\t{}".format(N Line))
loop(N + 1)
}
C.on_eof{}
}
}
loop(1)
6.3.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 (STR.default_newline), and encodes the str using the runtime default 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
:OUTPUT_PRINTER.require_from('kink/io/')
:CHARSET.require_from('kink/')
:Printer <- OUTPUT_PRINTER.new(stdout CHARSET.utf8 "\n")
Printer.print_line('foobar') # => foobar
6.3.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 (STR.default_newline), and encodes the str using the runtime default charset (CHARSET.default).
When encoding error happens on print_xxx methods, `stderr` writes replacement byte sequence instead, rather than resulting in an IO error. This is because, when printing an error message, it is better you can read the original message even incomplete, rather than reading a message describing the conversion error.
write, print, flush, close operations of stderr are synchronized, in order that multiple threads can output error messages to stderr.
6.3.1.14. Binding.repr¶
`repr` returns a str such as "(binding id_hash=42)".
6.3.2. BINDING.new¶
`new` makes a new binding.
6.3.3. BINDING.is?(Val)¶
`is?` returns whether the `Val` is a binding val.