Is it possible to define a string type that only contain strings of some format? Such as a RFC3339 timestamp
The same question goes to: is it possible to define range types such as only integers > 10
Kinda, but indirectly
You would use an opaque type and force the opaque type constructor to enforce the rules
Can you give me an example?
This is useful when I deal with cryptographic strings that has to follow certain format
I only want to validate them once after the IO and use the correct type afterwards
A niche use case for sure
So for the integer you would make an opaque type
AboveTenInt := I64
Then you would only expose a constructor that ensures it's valid
create: I64 -> Result AboveTenInt [NotAboveTen]
create = \i ->
if i > 10 then
Ok (@AboveTenInt i)
else
Err NotAboveTen
Then make sure any functions that modify an above ten int also deal with keeping it above ten or returning an error.
Is create
a special function?
Not particularly. It just has to be in the same module where AboveTenInt
is defined so it can access the wrapper function @AboveTenInt
.
So you protect wrapping and unwrapping of opaque types using the module boundary
As long as you have correct functions within the module, nothing outside the module can break the opaque type. They will only be able to modify it by calling those special opaque type functions.
some kind of like public/private functions in a imperative language
Essentially
anything that can produce an AboveTenInt is defined in the module
What if I just declare a literal as AboveTenInt outside the module?
Would have to go through create
. Though you could also make a version that panics instead of returns an error. That would be greater for constants, but dangerous otherwise.
Long term we want better support for compile time eval that could fold away and unwrap constants (with compile time asserts that they unwrap safely)
more like Rust macro or Zig comptime?
or just a built-in compiler feature?
Probably like zig comptime, but less powerful (no metaprogramming via passing around and manipulating types). So really just evaluation of constants and functions that have all inputs know at compile time.
Last updated: Jul 05 2025 at 12:14 UTC