3.5. kink/CONTROL

The mod which provides various control funs.

3.5.1. CONTROL.try($body $on_returned $on_raised)

`try` traps an exception raised by an invocation of $body.

Preconditions:

• $body must be a thunk fun

• $on_returned must be a fun which takes an argument, which is the result of $body

• $on_raised must be a fun which takes an exception raised in the invocation of $body

`try` invokes $body with no args.

If the invocation of $body terminates without raising an exception, `try` tail-calls $on_returned with the result of the invocation of $body.

If the invocation of $body terminates raising an exception, `try` tail-calls $on_raised with the exception.

Example:

:TRACE.require_from('kink/')
:CONTROL.require_from('kink/')

:attempt_divide_10_by <- {(:Divisor)
  CONTROL.try(
    { 10 // Divisor }
    {(:Result)
      stdout.print_line('result: {}'.format(Result))
    }
    {(:Exc)
      Exc.desc_iter.each{(:Line)
        stdout.print_line(Line)
      }
    }
  )
}

attempt_divide_10_by(2)
# Output:
#   result: 5

attempt_divide_10_by(0)
# Output:
#   exception traces: from oldest to newest
#   [startup]
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L236 C3 _startup_aux] -->_startup_aux(Args Dep)
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L219 C11 try] CONTROL.-->try(
#   [builtin:kink-mods/kink/CONTROL.kn L204 C19 reset] :handler = KONT.-->reset('kink/CONTROL-try'){
#   [builtin:kink-mods/kink/CONTROL.kn L205 C10 body] :R = -->body
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L221 C7 _start] -->_start(Non_opts Dep)
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L122 C3 if] -->if(Non_opts.empty?
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L135 C7 branch] -->branch(
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L160 C11 _run_script] -->_run_script($script_fun Script_args)
#   [builtin:kink-mods/kink/_startup/STARTUP.kn L113 C3 script_fun] -->script_fun(Binding)
#   [(stdin) L22 C1 attempt_divide_10_by] -->attempt_divide_10_by(0)
#   [(stdin) L5 C11 try] CONTROL.-->try(
#   [builtin:kink-mods/kink/CONTROL.kn L204 C19 reset] :handler = KONT.-->reset('kink/CONTROL-try'){
#   [builtin:kink-mods/kink/CONTROL.kn L205 C10 body] :R = -->body
#   [(stdin) L6 C10 op_intdiv] { 10 -->// Divisor }
#   exception message: Num.op_intdiv: zero division: 10 is divided by 0

3.5.2. CONTROL.while($cond $body)

CONTROL.while calls $body repeatedly while $cond returns true.

Preconditions:

• $cond must be a thunk fun which returns a bool

• $body must be a thunk fun

CONTROL.while first calls $cond with no args. If $cond returns true, CONTROL.while calls $body with no args. It repeats until Fun returns false.

Example:

:CONTROL.require_from('kink/')

:Vec <- [1 2 3]
CONTROL.while{ ! Vec.empty? }{
  :N = Vec.pop_front
  stdout.print_line(N.repr)
}
# Output:
#   1
#   2
#   3

3.5.3. CONTROL.with_finally($body)

`with_finally` provides functionality of resource releases at the end of the procedure which uses the resources.

`with_finally` calls $body with a fun, and returns the result of $body. Let $finally be the arg passed to $body.

Preconditions of $finally:

• $finally must be called within the corresponding invocation of $body.

• $finally takes a thunk.

When $finally is called with a thunk, the thunk is ensured to be called after the invocation of $body ends in the following ways:

(1) When the invocation of $body returns a val,

(2) When the invocation of $body is terminated by $break fun of CONTROL.with_break with a val, or

(3) When the invocation of $body raises an exception.

Example:

:CONTROL.require_from('kink/')
:CHARSET.require_from('kink/charset/')
:FILE.require_from('kink/io/')

:Text <- CONTROL.with_finally{(:finally)
  :In = FILE.open_to_read('/path/to/file')
  finally{ In.close }
  CHARSET.utf8.bin_to_str(In.read_all)
}
stdout.print(Text)  # prints the text in /path/to/file

In the example above, `In` is closed when exiting from `with_finally`, no matter whether `read_all` raises an exception or not.

$finally can be called multiple times. If $finally is called with thunks `T1`, `T2`, ,,, `Tn` respectively in that order, the thunks are called at the end of the invocation of $body in the order `Tn` ,,, `T2`, `T1`.

Example:

:CONTROL.require_from('kink/')

CONTROL.with_finally{(:finally)
  finally{ stdout.print_line('T1') }
  finally{ stdout.print_line('T2') }
  finally{ stdout.print_line('T3') }
}
# Output:
#   T3
#   T2
#   T1

TODO: elaborate on exception/jump handling.

3.5.4. CONTROL.with_break($body)

`with_break` provides functionality of parameterized non local exit.

`with_break` calls $body with a fun. Let $break be the fun.

Precondition of $break:

• $break takes an arbitrary argument.

• $break must be called within the corresponding invocation of $body.

When $break is called, it escapes from the corresponding invocation of `with_break`, and `with_break` returns the argument passed to $break.

If $break is not called within the invocation of `with_break`, `with_break` returns the result of $body.

Example:

:CONTROL.require_from('kink/')

:to_fizz_buzz <- {(:Num)
  CONTROL.with_break{(:break)
   if(Num % 15 == 0){ break('fizzbuzz') }
   if(Num % 3 == 0){ break('fizz') }
   if(Num % 5 == 0){ break('buzz') }
   Num.show
  }
}
1.up.take(20)
.map{(:N) to_fizz_buzz(N) }
.each{(:S) stdout.print(S + ' ') }
# => 1 2 fizz 4 buzz fizz 7 8 fizz buzz 11 fizz 13 14 fizzbuzz 16 17 fizz 19 buzz