I am new to SML (and programming, actually).
fun readlist (infile : string) =
let
val
ins = TextIO.openIn infile
fun loop ins =
case TextIO.inputLine ins of
SOME line => line :: loop ins
| NONE => []
in
loop ins before TextIO.closeIn ins
end ;
This is a program I encountered here. How do I use SOME and NONE, and how to use 'before'?
We write lists as a sequence of integers, separated by commas, all enclosed with two square brackets. So then we have valid int lists as [1, 2, 3] , [1, 5, 1, 5, 0] , and [] , with the latter representing the empty list. We also refer to the empty list as nil (in addition, you can type nil instead of [] in code).
The form ''a is a so-called equality type variable, which means that it can only be substituted by types that admit the use of the equality operator = (or <> ) on their values. For example, this function: fun contains(x, []) = false | contains(x, y::ys) = x = y orelse contains (x, ys)
fun nth(nil, _) = 0 | nth(x::xs, 0) = x; | nth(x::xs, y) = val list = [1, 2, 3]; nth(list, 0);
Standard Meta Language (SML) is a type-safe programming language that encapsulates numerous innovative ideas in programming language plan or design. It is a statically composed language, with an extensible type framework.
The option
data type is used if there is a possibility of something having no valid value.
For instance,
fun divide x y = if y == 0 then NONE else SOME (x / y)
could be used if you need to handle the special case of division by zero without resorting to exceptions.
TextIO.inputLine
returns NONE
when there is nothing more to read, and SOME l
, where l
is the line it has read, when there is.
before
is a low-precedence (the lowest of all) infix function that first evaluates its left hand side, then the right hand side, and then returns the value of the left hand side.
It has type 'a * unit -> 'a
, i.e. the right hand side is used only for its side effects.
In this case, it makes the code slightly more readable (and functional-looking) than the equivalent
fun readlist (infile : string) =
let
val ins = TextIO.openIn infile
fun loop indata =
case TextIO.inputLine indata of
SOME line => line :: loop indata
| NONE => []
val result = loop ins
in
TextIO.closeIn ins;
result
end
Some('a)
and None
are part of the Option
datatype. Option
is an algebraic or compound data structure found in SML's Basis Library. More on the Option data type at Wikipedia. The big idea is, allow a function to return the value None
when it doesn't make sense for the function to return a value of the type which the programmer really cares about.
In the case of your user defined function readlist
the important data is the string. But at some point, the program hits the end of the file and reads a value interpreted as EOF
instead of a string.
Think about TextIO.openIn
as a function that opens a stream and searches it for strings. Each time it finds a string, it returns an option(string)
. When it does not find a string, it returns None
. Because both are part of the Option(string)
datatype, TextIO.openIn
only returns a single type.
inputLine strm a stream] returns SOME(ln), where ln is the next line of input in the stream strm. Specifically, ln returns all characters from the current position up to and including the next newline (#"\n") character. If it detects an end-of-stream before the next newline, it returns the characters read appended with a newline. Thus, ln is guaranteed to always be new-line terminated (and thus nonempty). If the current stream position is the end-of-stream, then it returns NONE. It raises Size if the length of the line exceeds the length of the longest string.
A related concept in SML is user-defined datatypes. Both the option
datastructure and user-defined data types provide flexibility within SML's static type system in a somewhat similar manner to the way objects are used in statically typed object oriented languages.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With