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.

Example:

:CORE.require_from('kink/')
stdout.print_line(CORE.true.repr) # => true
stdout.print_line(true.repr)      # => true

3.6.1. CORE.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 “()”. nada is used when no meaningful val is needed. For example, Varref.op_store returns nada val.

stdout.print_line((:X <- 49).repr) # => nada

The only method nada val has is `repr`, which returns str "nada".

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.identity(Val)

`identity` returns `Val`.

Example:

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

3.6.5. CORE.val_id(Val)

`val_id` returns an int num which identifies `Val`.

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

Example:

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

3.6.6. CORE.var_syms(Val)

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

The result is a set of strs.

Example:

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

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

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

    # 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.8. 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.

Preconditions:

• 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.identity 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.

Example:

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

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

3.6.9. CORE.traces

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

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

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

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

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

3.6.15. CORE.branch(...[$cond1 $then1 $cond2 $then2 ,,,])

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 `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)

  # CORE.if 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)
    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, CORE.if might be a better choice.

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

Example: emulates "cat -n"

:loop <- {(:N)
  stdin.scan_line.for_maybe(
    {(: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.for_maybe(
    {(:Line)
      stdout.print("{}\t{}".format(N Line))
      loop(N + 1)
    }
    {}
  )
}
loop(1)

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

:BIN.require_from('kink/')
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

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

3.6.18. 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.19. type val

Every val has `repr` method.

V.repr

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

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

Example:

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