Typeprelude
record Type
Types as values.
Type.of(x) answers what a value IS, as something you can hold, print, compare, and take apart:
Type.of(42) # Type { name: "Integer", args: [] }
Type.of([1, 2]).to(String) # "[Integer]"
Type.of(x) == Type.of(y) # structural equality, like any record
Type.of(due).fields # ["year", "month", "day"]
The answer comes from the compiler where it can: a checked expression knows things a value cannot carry, such as the unused half of a Result or the element type of an empty list. Where the checker has no concrete answer: gradual code, --no-check, a value arriving from another process: the value itself is asked instead. That fallback is honest but lossy: an empty list has no element to inspect, and a Result only ever holds one side.
Fields
nameStringargs[Type]pureBool
module Type
Building and obtaining Type values.
function of
Returns the type of value.
The entry point to everything else here. Prefer it over matching on the Type record directly.
of(value)
Parameters
valueAny- the value to inspect
Returns: Type — its type
Examples
Type.of(42) # => Type { name: "Integer", args: [] }
Type.of([1, 2]).to(String) # => "[Integer]"
Type.of("hi").to(String) # => "String"
Reporting an unexpected value
IO.printError("expected a list, got ${Type.of(value).to(String)}")
function named
Builds a type from its name, for comparing against something you already know.
named(name)
Parameters
nameString- the type name
Returns: Type — the named type
Examples
Type.of(42) == Type.named("Integer") # => true
Checking that a value came back as expected
Type.of(parsed) == Type.named("Date")
function generic
Builds a type that takes arguments, from its name and those arguments.
Renamed from Type.with when with became the capability-substitution keyword (kexhq/kex#143).
generic(name, args)
Parameters
nameString- the type name
args[Type]- its type arguments
Returns: Type — the generic type
Examples
Type.of([1]) == Type.generic("List", [Type.named("Integer")]) # => true
function function
Builds a function type from its parameter types and its result type: in the order a signature is written.
function(params, result)
Parameters
params[Type]- the parameter types
resultType- the result type
Returns: Type — the function type
Examples
Type.function([Type.named("String")], Type.named("String")).to(String)
# => "String -> String"
function returnedBy
Returns the type a named function returns, as answered by the compiler.
Named functions only. A lambda or a function VALUE carries no signature at runtime, and an overloaded name has no single answer: both are compile errors rather than a guess.
returnedBy(function)
Parameters
functionAny- a named function
Returns: Type — its return type
Examples
Type.returnedBy(Date.parse).to(String) # => "Result<Date, TimeError>"
make Type
Everything else is a METHOD, not a module function: a module function is only reachable through UFCS in the interpreter, so Type.of(x).fields worked there and raised on BEAM.
to
Renders the type the way it is written in SOURCE, not the way it is stored.
A list reads as [Integer], a tuple as (Integer, String), an optional as String?, a map as {String: Integer}, and a function as its signature.
to(String)
Returns: String — the type, as source
Examples
Type.of([1, 2]).to(String) # => "[Integer]"
Type.of((1, "a")).to(String) # => "(Integer, String)"
Type.of(Just(1)).to(String) # => "Integer?"
Type.of("hi").to(String) # => "String"
An error message that names the type
die("cannot serialise a ${Type.of(value).to(String)}")