5.4. Typing

Kink language does not support static type system. However, in the perspective of programming practices, typing exists. For example, repr method of any value is expected to return a value of str type, and a str value is expected to have size method. Thus, you can write repr_size function as follows:

# (Val: any type) -> int num type
:repr_size <- {(:Val)
  Val.repr.size
}

stdout.print_line(repr_size(12345).repr)  # => 5
stdout.print_line(repr_size(['foo' 'bar']).repr)  # => 13

This chapter describes typing and type testing patterns of Kink.

5.4.1. Typing contracts

Typing can be considered as a contract between a component and its client. Here, components include the language and modules. Contracts should be specified in public documentation.

For example, in the language specification, a function expression is specified to produce a function value. It is a postcondition contract of a function expression. Thus, a program can assume the result of a function expression can be called, or has call method.

For another example, the first argument of print_line method of a printer value is specified to be a str. It is a precondition contract. Thus, if the value given to the argument is not a str value, the behavior of the method is not specified.

5.4.2. Type testing by intrinsic property

The runtime or a host procedure might test the type of a value by its intrinsic property, such as the runtime class of the value.

One example is checkfun abstract instruction, which ensures that the called value is a function. The check is probably done using an instrinsic property of the value, such as by testing the runtime class.

For another example, raise preloaded function takes a str value as the argument. Probably the function ensures that the argument is a str, by testing the runtime class.

5.4.3. Duck type testing

Another way of type testing is duck type testing.

duck type testing

Type testing based on what methods a value has.

For example, if rat type is expected to have numer, denom and repr methods, duck type testing can be done by ensuring a value has numer and denom variables.

Note

Every value has repr method. Thus, testing exisitence of repr method is not needed.

Usually, a set of methods can be extracted out of the trait. In the following example, A vec of method symbols is stored in Rat_var_syms variable. is? method does duck type testing comparing the variable symbols of the value to test, and Rat_var_syms.

# example/RAT.kn

:VAL.require_from('kink/')

# `new` makes a new rat value.
:new <- {(:Numer :Denom)
  new_val(
    ... Rat_trait
    'Numer' Numer
    'Denom' Denom
  )
}

# `is?` returns whether `Val` is a rat value.
# The type testing is done by duck type testing.
:is? <- {(:Val)
  VAL.var_syms(Val).have_all?(Rat_var_syms)
}

:Rat_trait <- [
  'numer' {[:R]() R.Numer }
  'denom' {[:R]() R.Denom }
  'repr' {[:R]()
    '(rat numer={} denom={})'.format(R.numer.repr R.denom.repr)
  }
]

# => ['numer' 'denom' 'repr']
:Rat_var_syms <- Rat_trait.chunk(2).map{([:Sym :Val]) Sym }

is? method can be used as follows:

:RAT.require_from('example/')

stdout.print_line(RAT.is?(RAT.new(10 20)).repr)  # => true
stdout.print_line(RAT.is?(nada).repr)  # => false