Like my last post on ATS , this is far from a full-fledged tutorial, but I think it will nonetheless be instructive to students of ATS.
Linear Types
Linear types are a relatively unfamiliar feature to most programmers; Rust is the only mainstream language that has them, but ATS gives the programmer finer control of how linear types, allowing the programmer to construct their own linear types in much the same way as ordinary types in an ML.
Linear types in this example accomplish essentially what is accomplished in Rust - they allow us to safely skirt garbage collection where performance is absolutely critical.
Example: Consuming Errors
To again use a "real world" example from polyglot, suppose we wish to count the number of lines in a file.
// Takes as an argument a string containing the full path to a file.
fun line_count(s: string): int =
let
var ref = fileref_open_opt(s, file_mode_r)
in
case+ ref of
| ~None_vt() => (println!("Warning: could not open file at " + s) ; 0)
| ~Some_vt(x) =>
begin
let
var viewstream: stream_vt(string) = $EXTRA.streamize_fileref_line(x)
var n: int = stream_vt_length(viewstream) - 1
val _ = fileref_close(x)
in
n
end
end
end
This will be mostly familiar to anyone with a Haskell background - Some
corresponds to Just
, and None
to Nothing
. But here we are matching on
a value with constructor Some_vt
rather than Some
, and for some reason prepending ~
to the
constructors.
This is because fileref_open_opt
returns a value with a linear
type. Option_vt
is just like Option
, except that it must always be consumed
in the scope that it is created. Linear types allow us to precisely pinpoint
where a value is needed, eliminating the need for garbage
collection (as in Rust). In this example, linear types force us to handle the error -
the ~
in the pattern match tells the compiler "immediately
free the memory after the right-hand side executes". Had we replaced the pattern
~Some_vt(x)
with _
, we would have seen a type error.
In practice, this means that we will be much more careful to avoid spurious
computation, e.g. filling
a list_vt(a)
with values we don't need.
Haskell has laziness to avoid spurious computation, but
it cannot benefit from unboxed data types at the same time. ATS is to my
knowledge the
only language that can offer both guarantees.