5.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
5.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`.
5.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
5.4.1.2. true¶
`true` returns the true bool val.
5.4.1.3. false¶
`false` returns the false bool val.
5.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
5.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)
  EXCEPTION.new(Msg).raise
}
5.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
5.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
5.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
5.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
5.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.
5.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{(: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/charset/')
: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)
5.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
:OUTPUT_PRINTER.require_from('kink/io/')
:CHARSET.require_from('kink/charset/')
:Printer <- OUTPUT_PRINTER.new(stdout CHARSET.utf8 "\n")
Printer.print_line('foobar') # => foobar
5.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).
5.4.1.14. Binding.repr¶
`repr` returns a str such as "(binding val_id=42)".
5.4.2. BINDING.new¶
`new` makes a new binding.
5.4.3. BINDING.is?(Val)¶
`is?` returns whether the `Val` is a binding val.