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.