Prelude: Built-in Types and Functions¶
Typist's prelude defines the primitive type hierarchy, standard type constructors, built-in effect labels, and type annotations for Perl's core functions. The prelude is installed automatically into every registry.
Primitive Types¶
| Type | Kind | Description |
|---|---|---|
Any |
* |
Top type. Every type is a subtype of Any |
Void |
* |
No meaningful return value |
Never |
* |
Bottom type. No values inhabit this type |
Undef |
* |
Perl's undef value |
Bool |
* |
Boolean (0/1 in boolean context) |
Int |
* |
Integer |
Double |
* |
Floating-point number |
Num |
* |
Numeric supertype |
Str |
* |
String |
Subtype Hierarchy¶
The subtyping relationships are:
Bool <: Int <: Double <: Num <: AnyStr <: AnyUndef <: AnyVoid <: AnyNever <: Tfor all typesT(bottom type)
Type Constructors¶
| Constructor | Kind | Description | Desugaring |
|---|---|---|---|
ArrayRef[T] |
* -> * |
Scalar reference to array | -- |
HashRef[K, V] |
* -> * -> * |
Scalar reference to hash | -- |
Tuple[T...] |
* -> ... -> * |
Fixed-length array reference | -- |
Ref[T] |
* -> * |
Scalar reference | -- |
Maybe[T] |
* -> * |
Optional value | T | Undef |
CodeRef[A -> R] |
* -> * |
Function reference | (A) -> R |
Array[T] |
* -> * |
List type (list context) | -- |
Hash[K, V] |
* -> * -> * |
List type (list context) | -- |
Handler[E] |
* -> * |
Effect handler | -- |
Array vs ArrayRef¶
Array[T] and ArrayRef[T] are distinct types:
ArrayRef[T]is a scalar reference ($ref = [1, 2, 3]). Use for variables and parameters.Array[T]is a list type representing list-producing expressions. Use for return types in list context.
The same distinction applies to Hash[K, V] vs HashRef[K, V].
Subtyping for Constructors¶
ArrayRefis covariant:ArrayRef[Int] <: ArrayRef[Num]HashRefis covariant in both parameters:HashRef[Str, Int] <: HashRef[Any, Num]Record <: HashRef[Str, V]when all field values are subtypes ofV
Built-in Effect Labels¶
| Label | Operations | Ambient | Description |
|---|---|---|---|
IO |
(none) | Yes | Standard I/O, system interaction, time, randomness |
Exn |
throw: (Any) -> Never |
Yes | Exception handling. Exn::throw($err) bridges to die |
Decl |
(none) | Yes | Type declarations (Typist's own typedef, struct, etc.) |
Ambient effects do not require an explicit handle block. They are automatically satisfied and are never reported as EffectMismatch errors.
Exn is the only built-in effect with an operation. Exn::throw($err) is installed as a sub that calls die.
Built-in Function Annotations¶
All built-in annotations are registered under the CORE:: namespace. These are the default type signatures for Perl's built-in functions.
I/O Functions (![IO])¶
| Function | Signature |
|---|---|
say |
(...Any) -> Bool ![IO] |
print |
(...Any) -> Bool ![IO] |
warn |
(...Any) -> Bool ![IO] |
open |
(...Any) -> Bool ![IO] |
close |
(Any) -> Bool ![IO] |
read |
(Any, Any, Int) -> Int ![IO] |
write |
(Any, Any, Int) -> Int ![IO] |
binmode |
(Any) -> Bool ![IO] |
eof |
(Any) -> Bool ![IO] |
seek |
(Any, Int, Int) -> Bool ![IO] |
tell |
(Any) -> Int ![IO] |
rand |
(...Num) -> Double ![IO] |
srand |
(...Int) -> Int ![IO] |
sleep |
(...Int) -> Int ![IO] |
time |
() -> Int ![IO] |
localtime |
(...Int) -> Any ![IO] |
gmtime |
(...Int) -> Any ![IO] |
require |
(Any) -> Bool ![IO] |
use |
(Any) -> Bool ![IO] |
system |
(...Any) -> Int ![IO] |
exec |
(...Any) -> Never ![IO] |
Exception Functions (![Exn])¶
| Function | Signature |
|---|---|
die |
(...Any) -> Never ![Exn] |
eval |
(Any) -> Any ![Exn] |
exit |
(...Int) -> Never ![Exn] |
String Functions (pure)¶
| Function | Signature |
|---|---|
length |
(Str) -> Int |
substr |
(Str, Int, ...Int) -> Str |
uc |
(Str) -> Str |
lc |
(Str) -> Str |
ucfirst |
(Str) -> Str |
lcfirst |
(Str) -> Str |
index |
(Str, Str, ...Int) -> Int |
rindex |
(Str, Str, ...Int) -> Int |
chomp |
(Any) -> Int |
chop |
(Any) -> Str |
chr |
(Int) -> Str |
ord |
(Str) -> Int |
hex |
(Str) -> Int |
oct |
(Str) -> Int |
quotemeta |
(Str) -> Str |
sprintf |
(Str, ...Any) -> Str |
Numeric Functions (pure)¶
| Function | Signature |
|---|---|
abs |
(Num) -> Num |
int |
(Num) -> Int |
sqrt |
(Num) -> Double |
log |
(Num) -> Double |
exp |
(Num) -> Double |
sin |
(Num) -> Double |
cos |
(Num) -> Double |
atan2 |
(Num, Num) -> Double |
Type/Value Introspection (pure)¶
| Function | Signature |
|---|---|
defined |
(Any) -> Bool |
ref |
(Any) -> Str |
wantarray |
() -> Bool |
caller |
(...Int) -> Any |
Array Functions (pure)¶
| Function | Signature |
|---|---|
scalar |
(Any) -> Int |
push |
(Any, ...Any) -> Int |
pop |
(Any) -> Any |
shift |
(...Any) -> Any |
unshift |
(Any, ...Any) -> Int |
splice |
(Any, ...Any) -> Any |
reverse |
(...Any) -> Any |
sort |
(...Any) -> Any |
map |
(Any, ...Any) -> Any |
grep |
(Any, ...Any) -> Any |
Hash Functions (pure)¶
| Function | Signature |
|---|---|
keys |
(Any) -> Any |
values |
(Any) -> Any |
each |
(Any) -> Any |
delete |
(Any) -> Any |
exists |
(Any) -> Bool |
String Matching (pure)¶
| Function | Signature |
|---|---|
split |
(Any, ...Any) -> Any |
join |
(Str, ...Any) -> Str |
pack |
(Str, ...Any) -> Str |
unpack |
(Str, Str) -> Any |
Typist Declaration Functions (![Decl])¶
| Function | Signature |
|---|---|
typedef |
(...Any) -> Void ![Decl] |
newtype |
(...Any) -> Void ![Decl] |
effect |
(...Any) -> Void ![Decl] |
typeclass |
(...Any) -> Void ![Decl] |
instance |
(...Any) -> Void ![Decl] |
declare |
(Str, Str) -> Void ![Decl] |
datatype |
(...Any) -> Void ![Decl] |
struct |
(...Any) -> Void ![Decl] |
Overriding Prelude Annotations¶
Use declare to override any prelude annotation with a custom one:
The last declare for a given name wins (simple replacement). This works because register_function uses plain assignment -- later writes overwrite earlier entries in the registry.
Override is useful when:
- You want a more specific signature than the prelude provides (e.g., narrowing
(...Any)to specific types). - You define a custom effect label and want builtins to use it instead of
IO. - You need to annotate a function that is not in the prelude.
declare can annotate any function, not just builtins:
Gradual Typing Defaults¶
When no annotation is present, Typist applies these defaults:
- Unannotated functions: treated as
(Any...) -> Anywith no effect constraints (pure). - Unannotated variables: type is inferred from the initializer, or
Anyif no initializer. - Partially annotated functions (return type present but not all params): return type is checked, unknown params are
Any.
The gradual typing principle: no annotation = no constraint. Types use Any (compatible with all types in both directions), effects use pure (no effects declared, so no effect checking applies).