Docs / Types

Types

Kex models data directly: records for product types, type for unions, ? for optional values, and Result for fallible flows.

Records

record.kex
record Temperature do
  celsius : Float

  static do
    let Freezing = Temperature { celsius: 0.0 }
  end
end

let boiling = Temperature { celsius: 100.0 }
let freezing = Temperature.Freezing

Sum types

union.kex
type ParseError = InvalidFormat(String) | Overflow | EmptyInput

let describe(e: ParseError) -> String do
  match e do
    InvalidFormat(s) -> "bad input: ${s}"
    Overflow         -> "out of range"
    EmptyInput       -> "got nothing"
  end
end

Optional

T? is sugar for an optional value. Constructors are Just(value) and None. flatMap chains optional computations.

optional.kex
# Using bare Optional<T>
let findUserTodo(todos, user, todo) -> Optional<Todo>
  let todo = todos.find { |t| t.id == todo.id }
  return None if todo.userId != user.id

  return Just(todo)
end

# Using T? sugar
let findUser(users, name) -> User? do
  return users.find { |u| u.name == name }
end


let email = users
  .find(&.adult?)
  .flatMap(&.email)        # String?

Result

Result<T, E> is either Ok(value) or Error(reason). The ? operator returns early on an Error, unwrapping an Ok.

result.kex
let parsePort(s: String) -> Result<Int, ParseError> do
  return Error(EmptyInput) if s.empty?
  
  match Integer.parse(s) do
    Ok(n)    -> do
      return Error(Overflow) if n > 65535
      return Ok(n)
    end
    Error(_) -> Error(InvalidFormat(s))
  end
end

Result<T, E> can also be simplified as T or! E.

result.kex
let parsePort(s: String) -> Int or! ParseError do
  return Error(EmptyInput) if s.empty?
  
  match Integer.parse(s) do
    Ok(n)    -> do
      return Error(Overflow) if n > 65535
      return Ok(n)
    end
    Error(_) -> Error(InvalidFormat(s))
  end
end