PreML is a preprocessor for Standard ML. It’s aim is to extent the language with some useful syntactic sugar.
It is not the aim of PreML to turn SML into a new language. Thus I try to keep the rewritings simple and easy to understand.
PreML will try to preserve your code as good as it can; Error messages from your compiler will always refer to the correct line, and code parts that doesn’t use PreML’s features will not be changed.
Right now PreML only compiles under MLTon. If you have MLTon installed you can build PreML simply by simply typing
After which PreML can be found in the
There’s also an
install goal which installs the binaries in
/usr/local/bin, so you probably need to be root to do that.
PreML also installs through Smackage. I think it’s a really cool thing the Smacakge guys have going so I suggest you use this method.
Assuming you have a working installation of Smackage (and if you don’t: go to http://github.com/standardml/smackage to see how to get it) installation should be as easy as
smackage source preml git git://github.com/mortenbp/PreML.git smackage refresh smackage get preml smackage make preml smackage make preml smackage-install
This was tested with Smackage v0.6.0.
After installation two programs are available:
preml which is the
premlton which is just a shellscript wrapper for MLTon.
to get a help message.
The MLTon wrapper
premlton makes the use of PreML more or less transparent
to the user:
MyProject.mlb, produce an executable
MyProject from the
result, and clean up the preprocessed files.
PreML understands nine kinds of syntactic sugar as of this writing (v1.4.1).
Examples of most forms can be found in the
A syntax very much like Haskells
do syntax is supported. A
begins with either just
do with X where
X is some structure
return, and ends with
Each line in a
do block except the first and the last must begin with a
do with List ; x <- [1, 2, 3] ; y <- [4, 5, 6] ; return (x + y) end
open the result of a functor application
Instead of having to bind the result of a functor application to a structure and then opening it one can simply write
F is a functor name and
X can be anything usually allowed as a
Be aware that PreML simply invents a new structure name, binds it to the
result and the opens it. Thus this syntax is not allowed in
Extend a structure through a functor
A use of functors is to derive functionality based on an
interface. Structures implementing that interfaces (a
compare function for
instance) can then get the derived functionality through the functor for
An example functor:
functor Range (eqtype t val next : t -> t) = struct fun range (a, b) = if a = b then [a] else a :: range (next a, b) end
and some structure implementing the interface
structure MyInt = struct open Int eqtype t = int fun next n = n + 1 end structure MyChar = struct open Char eqtype t = char fun next c = chr (Rod c + 1) end
Now we have two structures implementing the interface of our functor. To extend those structure we can write
extend MyInt as (Range) extend MyChar as (Range)
Now the structures have both the
next and the
range function (and all
the other functions pulled in from
Another possibility is to extend the structures as we’re defining them.
Then the definition of
structure MyInt = struct (Range) open Int eqtype t = int fun next n = n + 1 end
It is possible to extend structures through more than one functor at a time. Simply put a list of functors in the parenthesis:
extend Foo as (Bar, Baz) structure Foo = struct (Bar, Baz) ... end
Baz functor will then be called with union of the original structure
and the output from
Fail exceptions with a file position
raise Fail "foo bar baz"
one can write
raise FailWithPosition "foo bar baz"
The result is that the position of the error message (which is not necessarily the same as where the exception is raised) will be prepended to it.
The resulting error message will look like this:
! Uncaught exception: ! Fail "/tmp/sml3238ZQE(26:24): foo bar baz"
which says that the exception is declared on line 26 in file
include has been overloaded, such that if what follows is
enclosed in quotation marks it will be treated as a (relative) file path and
included verbatim. More than one file can be included at a time.
If the word
singleline (no quotation marks) follows immediately after
include the included file(s) will be placed on a single line in order to
preserve error message positions.
It goes without saying that debugging can be very hard in the event that the included file(s) is responsible for the error.
Say one needs values
baz from structure
Qux. One can
open (foo, bar, baz) Qux
Note that this only works for values. PreML does not do type checking so it
can’t know if
bar is a value, type, exception or datatype.
PreML supports Haskell style list comprehensions.
val xs = MyInt.range(~5, 5) val foo = [x | x <- xs, x > 0] val bar = [x * y | x <- xs, y <- xs, y < x]
Partially applied tuple constructors
Again inspired by Haskell tuples need not be fully applied.
val a = (,42) 42 val b = (42,) 42 val c = (,) 42 42 val d = (42,,42) 42 fun e x = (,x,) val f = e 41 42 43 val xs = map (42,) [1,2,3]
Included with PreML is the file
sml-defs.el which modifies Emacs’ sml-mode
to work with the
do notation. On my system the file resides in
When using sml-mode in Emacs you can have your interactive interpreter
preprocess your buffer before running it by putting the following in your
(setq sml-use-command (concat "local " "val filei = \"%s\" " "val fileo = filei ^ \".preml\" " "val _ = OS.Process.system (\"preml \\\"\" ^ filei ^ \"\\\"\") " "val _ = use fileo " "val _ = OS.FileSys.remove fileo " "in end") )