3.6. kink/CORE

kink/CORE mod provides funs which are commonly used by most programs.

Funs of CORE mod are included in env vals. Thus most programs do not have to require CORE mod explicitly.


stdout.print_line(CORE.true.repr) # => true
stdout.print_line(true.repr)      # => true

3.6.1. CORE.nada

nada returns the nada val, which is identical to the result of the expression “()”.

3.6.2. CORE.true

CORE.true returns the true bool val.

3.6.3. CORE.false

CORE.false returns the false bool val.

3.6.4. CORE.id(Val)

CORE.id returns Val.


stdout.print_line(id(id(id('foo')))) # => foo

3.6.5. CORE.new_val(Sym1 Val1 Sym2 Val2 ,,,)

CORE.new_val makes a new val, assigning vals to corresponding vars specified by the syms.

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.


: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)
    # reuse Rational_trait, which is common to all rational vals
    ... Rational_trait

    # Numer and Denom differ for each rational val
    '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

3.6.6. CORE.require(Mod_name ...[$config={}])

CORE.require produces the mod specified by Mod_name, initializing the mod if it is the first attempt for the mod.


• Mod_name must be a str

• $config, if given, must be a fun which takes a conf val

The conf val provides the following methods:

• C.on_success($success_cont) : uses $success_cont as the success cont. If not called, CORE.id is used as the default success cont.

• C.on_not_found($not_found_cont) : uses $not_found_cont as the not found cont. If not called, the default not found cont raises an exception when the mod is not found.

• C.on_compile_error($compile_error_cont) : uses $compile_error_cont as the compile error cont. If not called, the default compile error cont raises an exception when the program of the mod contains a compile error.

Result of CORE.require:

• If the mod is successfully loaded, the success cont is tail-called with the mod. Subsequent attempts of CORE.require provide the same mod for the same mod name.

• If the mod is not found, the not found cont is tail-called with no args.

• If the program of the mod contains a compile error, the compile error cont is tail-called with (Msg, From_loc, To_loc) where Msg is the error message of the compile erorr, From_loc the loc of the beginning of the compile error, and To_loc is the loc of the end of the compile error.

• If an exception is thrown while initializing the mod, such as because of an IO error, the exception is propagated to the caller of CORE.require.


:FLAT_SET <- require('kink/container/FLAT_SET')
stdout.print_line(FLAT_SET.of('foo' 'bar' 'baz').repr) # => Flat_set("bar" "baz" "foo")

Usually, using Varref.require_from is an easier way.

stdout.print_line(FLAT_SET.of('foo' 'bar' 'baz').repr) # => Flat_set("bar" "baz" "foo")

3.6.7. CORE.traces

CORE.traces returns a vec of traces in the execution stack, from the bottom to the top.

3.6.8. CORE.raise(Msg)

CORE.raise raises an exception with Msg as the message, and the current traces as the traces.

Msg must be a str.

CORE.raise can be roughly defined in the next pseudo Kink code.

:raise <- {(:Msg)
  reraise(Msg traces)

3.6.9. CORE.reraise(Msg Traces)

`reraise` raises an exception with `Msg` as the message, and `Traces` as the traces.


• `Msg` must be a str.

• `Traces` must be a vec of traces.

`reraise` can be roughly defined in the next pseudo Kink code.


:reraise <- {(:Msg :Traces)
  :Cleanups = DYN.vals('kink/CONTROL-cleanup').reverse
    { KONT.shift('kink/CONTROL-try'){
        :Base_cleanup_count = DYN.vals('kink/CONTROL-cleanup').size
        :Popped_cleanups = Cleanups.drop_back(Base_cleanup_count)
          EVALUATOR.run({ cleanup } {} {})
          on_raised(Msg Traces)
    { Cleanups.each{(:cleanup)
        EVALUATOR.run({ cleanup } {} {})
      __terminate_failure__(Msg Traces.dup)

Where __terminate_failure__ is a function to perform terminate-failure operation defined in Language specification → Execution chapter.

3.6.10. CORE.branch($cond1 $body1 $cond2 $body2 ,,,)

CORE.branch does a multiway branch.

First, CORE.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, CORE.branch raises an exception.

Second, CORE.branch tail-calls the body 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.

$body1, $body2, ,,, must take no args.


:Sorted_nums <- [1 2 3 5 8 11 19 30 49 89]

# returns whether Sorted_nums contains N, by binary search algorithm
:contain? <- {(:N)

  # Bool.if_else and CORE.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)
    (From < To).if_else(
      { :Ind = (From + To) // 2
        :T = Sorted_nums.get(Ind)
          { 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 two way branch, Bool.if_else may be a better choice.

If you do a one way conditional execution, Bool.if_true may be a better choise.

3.6.11. CORE.stdin

CORE.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

:Bin <- stdin.read_all

Example: emulates "cat -n"

:loop <- {(:N)
      stdout.print("{}\t{}".format(N Line))
      loop(N + 1)

Example: scan lines from the stdin decoding the byte stream as UTF-8

:Scanner <- WRAP_SCANNER.new(stdin CHARSET.utf8)
:loop <- {(:N)
      stdout.print("{}\t{}".format(N Line))
      loop(N + 1)

3.6.12. CORE.stdout

CORE.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

stdout.write_bin(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

:Printer <- WRAP_PRINTER.new(stdout CHARSET.utf8 "\n")
Printer.print_line('foobar') # => foobar

3.6.13. CORE.stderr

CORE.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).

3.6.14. type val

All vals contain the following methods.


`val_id` returns an int num which identifies V.

Invocations of `val_id` for the same val always return the same num. Invocations of `val_id` for different vals always return different nums. Therefore, identity of the val can be tested using the result of `val_id`.


:test_identity <- {(:X :Y)
  (X.val_id == Y.val_id).if_else(
    { stdout.print_line('same') }
    { stdout.print_line('different') }
:V <- new_val
test_identity(V V)        # => same
test_identity(V new_val)  # => different


`var_syms` returns the set of syms of the vars of V.

The result is a set of strs.


:V <- new_val('Data' 42 'fun' {})
stdout.print_line(V.var_syms.repr) # => Flat_set("Data" "fun" "repr" "val_id" "var_syms")


`repr` returns the str representation of V, used for debugging purpose.

`repr` is expected to be redefined for each type of vals.


stdout.print_line(new_val.repr) # => Val(val_id=5)
stdout.print_line('foo'.repr)   # => "foo"
stdout.print_line(42.repr)      # => 42