Created
January 23, 2019 02:42
-
-
Save sourabhxyz/86fd1039349399d6e73c2ed0d7e0e040 to your computer and use it in GitHub Desktop.
Cheat Sheet For SML
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* SML notes *) | |
when writing in editor, semicolon is not required but when typing in terminal we need | |
semicolon | |
val negative_number = ~15 (* Yeah, unary minus uses the 'tilde' symbol *) | |
(* | |
- Math.e; | |
val it = 2.71828182846 : real | |
- Math.cos (Math.pi/2.0); | |
val it = 0.0 : real | |
- Math.sqrt (49.01); | |
val it = 7.00071424927 : real | |
- Int.min (ceil 3.4, floor 3.4); | |
val it = 3 : int | |
*) | |
(* Optionally, you can explicitly declare types. This is not necessary as | |
ML will automatically figure out the types of your values. *) | |
val e = 2.718 : real | |
(* | |
- val a = 0 and b = 1 and c = 7.8; | |
val a = 0 : int | |
val b = 1 : int | |
val c = 7.8 : real | |
*) | |
- let | |
= val c = 3 and d = 4 (* Two local, independent bindings. *) | |
= in | |
= (c * d) div 3 | |
= end; | |
val it = 4 : int | |
- let val e = 5 and f = e+1 in e+f end; (* Error! *) | |
stdIn:24.23 Error: unbound variable or constructor: e | |
- let | |
= val c = 3; val d = c+1; (* These bindings are dependent *) | |
= in | |
= (c * d) div 3 | |
= end; | |
val it = 4 : int | |
- let | |
= val c = 3 | |
= in | |
= let val d = c+1 in (c * d) div 3 end | |
= end; | |
val it = 4 : int | |
(* Floating-point numbers are called "reals". *) | |
val tau = 2.0 * pi (* You can multiply two reals *) | |
val twice_rent = 2 * rent (* You can multiply two ints *) | |
(* val meh = 1.25 * 10 *) (* But you can't multiply an int and a real *) | |
val yeh = 1.25 * (Real.fromInt 10) (* ...unless you explicitly convert | |
one or the other *) | |
(* +, - and * are overloaded so they work for both int and real. *) | |
(* The same cannot be said for division which has separate operators: *) | |
val real_division = 14.0 / 4.0 (* gives 3.5 *) | |
val int_division = 14 div 4 (* gives 3, rounding down *) | |
val int_remainder = 14 mod 4 (* gives 2, since 3*4 = 12 *) | |
(* ~ is actually sometimes a function (e.g. when put in front of variables) *) | |
val negative_rent = ~(rent) (* Would also have worked if rent were a "real" *) | |
(* andalso is same as && in c, orelse is same as || in c, not is the negation) | |
(* Many values can be compared using equality operators: = (==) and <> (!=) *) | |
val pays_same_rent = (rent = 1300) (* false *) | |
val is_wrong_phone_no = (phone_no <> 5551337) (* false *) | |
(* Actually, most of the parentheses above are unnecessary. Here are some | |
different ways to say some of the things mentioned above: *) | |
fun is_large x = x > 37 (* The parens above were necessary because of ': int' *) | |
val is_sad = not has_something | |
val pays_same_rent = rent = 1300 (* Looks confusing, but works *) | |
val is_wrong_phone_no = phone_no <> 5551337 | |
val negative_rent = ~rent (* ~ rent (notice the space) would also work *) | |
(* Parentheses are mostly necessary when grouping things: *) | |
val some_answer = is_large (5 + 5) (* Without parens, this would break! *) | |
(* val some_answer = is_large 5 + 5 *) (* Read as: (is_large 5) + 5. Bad! *) | |
(* Besides booleans, ints and reals, Standard ML also has chars and strings: *) | |
val foo = "Hello, World!\n" (* The \n is the escape sequence for linebreaks *) | |
val one_letter = #"a" (* That funky syntax is just one character, a *) | |
val combined = "Hello " ^ "there, " ^ "fellow!\n" (* Concatenate strings *) | |
val _ = print foo (* You can print things. We are not interested in the *) | |
val _ = print combined (* result of this computation, so we throw it away. *) | |
(* val _ = print one_letter *) (* Only strings can be printed this way *) | |
val bar = [ #"H", #"e", #"l", #"l", #"o" ] (* SML also has lists! *) | |
(* val _ = print bar *) (* Lists are unfortunately not the same as strings *) | |
(* Fortunately they can be converted. String is a library and implode and size | |
are functions available in that library that take strings as argument. *) | |
val bob = String.implode bar (* gives "Hello" *) | |
val bob_char_count = String.size bob (* gives 5 *) | |
val _ = print (bob ^ "\n") (* For good measure, add a linebreak *) | |
(* Lists can only contain one kind of thing... *) | |
The notation with the square brackets is just a special syntax of building up lists using the constructors :: ("::" is called cons operator) and nil. | |
- 1::2::3::4::nil; | |
val it = [1,2,3,4] : int list | |
- (8,true)::(5,false)::nil; | |
val it = [(8,true),(5,false)] : (int * bool) list | |
- (2::4::6::nil) :: (2::3::5::7::nil) :: (2::4::8::16::32::nil) :: nil; | |
val it = [[2,4,6],[2,3,5,7],[2,4,8,16,32]] : int list list | |
- val a = 4; (* Global binding to a. *) | |
val a = 4 : int | |
- let val a = 5 in a+6 end; (* Local binding to a. *) | |
val it = 11 : int | |
- a; (* No change in a. *) | |
(* You can have lists of any kind *) | |
val number_count = List.length numbers (* gives 7 *) | |
(* Lists of the same kind can be appended using the @ ("append") operator *) | |
val guest_list = [ "Mom", "Dad" ] @ [ "Aunt", "Uncle" ] | |
(* If you have many lists of the same kind, you can concatenate them all *) | |
val everyone = List.concat groups (* [ "Alice", "Bob", "Huey", ... ] *) | |
(* val bad_list = [ 1, "Hello", 3.14159 ] : ??? list *) | |
(* We can give new names to types. This is called type aliasing *) | |
type vector2d = real * real | |
val unitx : vector2d = (1.0,0.0) (* unit vector along x-axis *) | |
val unity : vector2d = (0.0,1.0) (* unit vector along y-axis *) | |
(* Tuples, on the other hand, can contain a fixed number of different things *) | |
(* | |
val person1 = ("Simon", 28, 3.14159) (* : string * int * real *) | |
- ( 2 ); (* No such thing as a one-tuple *) | |
val it = 2 : int | |
- ((())); | |
val it = () : unit | |
- ((2,3,4), (true,3.3)); | |
val it = ((2,3,4),(true,3.3)) : (int * int * int) * (bool * real) | |
*) | |
(* You can even have tuples inside lists and lists inside tuples *) | |
val mixup = [ ("Alice", 39), | |
("Bob", 37), | |
("Eve", 41) ] (* : (string * int) list *) | |
val good_bad_stuff = | |
(["ice cream", "hot dogs", "chocolate"], | |
["liver", "paying the rent" ]) (* : string list * string list *) | |
(* Records are structrured data types of heterogeneous elements that are labeled. | |
The labels allow elements to be written in any order. *) | |
val rgb = { r=0.23, g=0.56, b=0.91 } (* : {b:real, g:real, r:real} *) | |
(* You don't need to declare their slots ahead of time. Records with | |
different slot names are considered different types, even if their | |
slot value types match up. For instance... *) | |
val Hsl = { H=310.3, s=0.51, l=0.23 } (* : {H:real, l:real, s:real} *) | |
val Hsv = { H=310.3, s=0.51, v=0.23 } (* : {H:real, s:real, v:real} *) | |
(* ...trying to evaluate `Hsv = Hsl` or `rgb = Hsl` would give a type | |
error. While they're all three-slot records composed only of `real`s, | |
they each have different names for at least some slots. *) | |
(* You can use hash notation to get values out of tuples. *) | |
val H = #H Hsv (* : real *) | |
val s = #s Hsl (* : real *) | |
(* | |
- {a = 2.3, b = {a="s",c=45}}; | |
val hi = {a=2.3,b={a="s",c=45}} : {a:real, b:{a:string, c:int}} | |
#a (#b check); will give s | |
- {one = (2.3, "string")}; | |
val it = {one=(2.3,"string")} : {one:real * string} | |
*) | |
((* Pattern matching is a funky part of functional programming. It is an | |
alternative to if-sentences. A function defined using a list of pattern matching rules "tries" each pattern and gives out the value corresponding to the first case that | |
matched. *) | |
(* Pattern matching is also possible on composite types like tuples, lists and | |
records. Writing "fun solve2 (a, b, c) = ..." is in fact a pattern match on | |
the one three-tuple solve2 takes as argument. Similarly, but less intuitively, | |
you can match on a list consisting of elements in it (from the beginning of | |
the list only). *) | |
(* PATTERNS: | |
- val (a,b,c) = (1,3,6); (* Matching in declaration *) | |
val a = 1 : int | |
val b = 3 : int | |
val c = 6 : int | |
- val {1=a, 2=b, 3=c} = {1=1,2=3,3=6}; | |
val a = 1 : int | |
val b = 3 : int | |
val c = 6 : int | |
- val {abscissa=x, ordinate=y} = {ordinate=3.2, abscissa=1.2}; | |
val x = 1.2 : real | |
val y = 3.2 : real | |
- val {b=x, ...} = {a=2, b="s", c=3.4, d=[1,2]}; | |
val x = "s" : string | |
- val (x,_,y) = (1,2,3); (* wildcard pattern *) | |
(* _ is a match everything pattern often called wildcard pattern. *) | |
val x = 1 : int | |
val y = 3 : int | |
- val head :: _ = [1, 2, 3]; | |
stdIn:25.1-25.26 Warning: binding not exhaustive | |
head :: _ = ... | |
val head = 1 : int | |
- val x as (fst,snd) = (2, true); (* layered pattern *) | |
val x = (2,true) : int * bool | |
val fst = 2 : int | |
val snd = true : bool | |
- val list as head :: tail = [1, 2, 3]; | |
stdIn:27.1-27.37 Warning: binding not exhaustive | |
list as head :: tail = ... | |
val list = [1,2,3] : int list | |
val head = 1 : int | |
val tail = [2,3] : int list | |
Only certain functions, constructors, can be used in patterns. :: is a constructor, | |
hence it can be use in a pattern. @ is not. Neither is +. | |
*) | |
(* Functions! *) | |
- (fn x => x+1); (* A function object: successor function *) | |
val it = fn : int -> int | |
- val f = (fn x => x+1); (* Bind f to the successor function *) | |
val f = fn : int -> int | |
- fun f (n) = n+1; (* Same function, alternative syntax. *) | |
val f = fn : int -> int | |
- f; | |
val it = fn : int -> int | |
- f (2); (* Function application *) | |
val it = 3 : int | |
- f 2; (* Parens not needed, if arg a token. *) | |
val it = 3 : int | |
- f 2 * 4; (* Application binds tightest. *) | |
val it = 12 : int | |
- (fn x => x+1) 8; (* Name not needed to apply to arg *) | |
val it = 9 : int | |
- [fn n=>n+1, fn m=>2*m, fn n=>n div 2]; | |
val it = [fn,fn,fn] : (int -> int) list | |
- (fn n=>2*n,fn r=>ceil r); | |
val it = (fn,fn) : (int -> int) * (real -> int) | |
- fun h (x) = (f x, g x); h (4); h (7); (* functions and tuples. *) | |
val h = fn : int -> int * int | |
val it = (6,12) : int * int | |
val it = (9,21) : int * int | |
- fun j (x) = [f x, g x]; j (4); j (7); (* functions and lists. *) | |
val j = fn : int -> int list | |
val it = [6,12] : int list | |
val it = [9,21] : int list | |
- fun fact (n) = if n=0 then 1 else n*fact(n-1); | |
- val fact' = (fn n => if n=0 then 1 else n*fact'(n-1)); (* Error! *) | |
stdIn:15.43-15.48 Error: unbound variable or constructor: fact' | |
- val rec fact = (fn n => if n=0 then 1 else n*fact(n-1)); | |
val fact = fn : int -> int | |
- fun (* mutually recursive function definitions *) | |
= odd (n) = if n=0 then false else even (n-1) | |
= and | |
= even (n) = if n=0 then true else odd (n-1); | |
val odd = fn : int -> bool | |
val even = fn : int -> bool | |
- fun (* mutually recursive function definitions *) | |
= take nil = nil | take (x::xs) = x::(skip xs) | |
= and | |
= skip nil = nil | skip (x::xs) = take xs; | |
val take = fn : 'a list -> 'a list | |
val skip = fn : 'a list -> 'a list | |
- take [1,2,3]; skip [1,2,3,4]; | |
val it = [1,3] : int list | |
val it = [2,4] : int list | |
- fun l (x as (fst,snd)) = (x,fst div snd); (* layered patterns *) | |
val l = fn : int * int -> (int * int) * int | |
- fun switch (x,y) = (y,x); | |
val switch = fn : 'a * 'b -> 'b * 'a | |
- switch (2, "abc"); | |
val it = ("abc",2) : string * int | |
- nil; | |
val it = [] : 'a list | |
(* Important list functions *) | |
- null; | |
val it = fn : 'a list -> bool | |
- hd; | |
val it = fn : 'a list -> 'a | |
- rev; (* to reverse the list *) | |
- tl; (* returns the remaining of the list except the head *) | |
val it = fn : 'a list -> 'a list | |
- fun member (x, nil) = false | member (x, y::ys) = x=y orelse member (x,ys); | |
val member = fn : ''a * ''a list -> bool | |
- fun insert (x, nil) = [x] | |
= | insert (x,y::ys) = if x<y then x::y::ys else y :: insert (x,ys); | |
val insert = fn : int * int list -> int list | |
- fun merge (xs,nil) = xs | |
= | merge (nil,ys) = ys | |
= | merge (x::xs,y::ys) = if x<y then x :: merge(xs,y::ys) else y :: merge(x::xs,ys); | |
val merge = fn : int list * int list -> int list | |
- val const7 = (fn x => fn y => 7); (* Several ways to define the same thing ... *) | |
val const7 = fn : 'a -> 'b -> int | |
- fun const7 x = (fn y => 7); | |
val const7 = fn : 'a -> 'b -> int | |
- fun const7 x y = 7; | |
val const7 = fn : 'a -> 'b -> int | |
- fun const7 _ _ = 7; | |
val const7 = fn : 'a -> 'b -> int | |
- fun f (x) = (fn (y,z) => if x=0 then y+z else x*(y+z)); | |
val f = fn : int -> int * int -> int | |
- fun f (x) (y,z) = if x=0 then y+z else x*(y+z); | |
val f = fn : int -> int * int -> int | |
- val times = (fn x => (fn y => x * y)); | |
val times = fn : int -> int -> int | |
- fun comp (f,g) = (fn x => f (g (x))); | |
val comp = fn : ('a -> 'b) * ('c -> 'a) -> 'c -> 'b | |
- fun comp (f,g) (x) = f(g(x)); | |
val comp = fn : ('a -> 'b) * ('c -> 'a) -> 'c -> 'b | |
- val f = comp (Math.sin, Math.cos); | |
val f = fn : real -> real | |
- val g = Math.sin o Math.cos; (* Composition "o" is predefined *) | |
val g = fn : real -> real | |
- f(0.25); | |
val it = 0.824270418114 : real | |
- g(0.25); | |
val it = 0.824270418114 : real | |
- val curry = (fn f => (fn a => fn b => f(a,b))); | |
val curry = fn : ('a * 'b -> 'c) -> 'a -> 'b -> 'c | |
- fun curry (f) (a) (b) = f(a,b); | |
val curry = fn : ('a * 'b -> 'c) -> 'a -> 'b -> 'c | |
- fun curry f a b = f (a, b) | |
- fun uncurry f (a,b) = f a b; | |
val uncurry = fn : ('a -> 'b -> 'c) -> 'a * 'b -> 'c | |
(* in uncurry "= f a b" means ((f a) b) *) | |
- fun add (u,v) = u + v | |
- fun addp u v = u + v | |
(* val add : int * int -> int | |
val addp : int -> (int -> int) *) | |
- val addp1 = curry add; | |
val addp1 = fn : int -> int -> int | |
- val add1 = uncurry addp; | |
val add1 = fn : int * int -> int | |
*) | |
(* Algebraic Data types *) | |
- datatype color = Red | Blue | Green; | |
datatype color = Blue | Green | Red | |
- Blue; Red; Green; | |
val it = Blue : color | |
val it = Red : color | |
val it = Green : color | |
(* Here is a function that takes one of these as argument *) | |
fun say(col) = | |
if col = Red then "You are red!" else | |
if col = Green then "You are green!" else | |
if col = Blue then "You are blue!" else | |
raise Fail "Unknown color" | |
(* We did not include the match arm `say _ = raise Fail "Unknown color"` | |
because after specifying all three colors, the pattern is exhaustive | |
and redundancy is not permitted in pattern matching *) | |
val _ = print (say(Red) ^ "\n") | |
(* Datatypes are very often used in combination with pattern matching *) | |
fun say Red = "You are red!" | |
| say Green = "You are green!" | |
| say Blue = "You are blue!" | |
- datatype bool = true | false; | |
datatype bool = false | true | |
- fun not (true) = false | not (false) = true; | |
val not = fn : bool -> bool | |
- datatype numbs = i of int | r of real; | |
datatype numbs = i of int | r of real | |
- [i 3, r 3.4, i 5, i 9, r 5.4]; | |
val it = [i 3,r 3.4,i 5,i 9,r 5.4] : numbs list | |
- fun isum nil = 0 | |
= | isum ((i n)::rest) = n+(isum rest) | isum (_::rest) = isum (rest); | |
val isum = fn : numbs list -> int | |
- datatype shape = Rectangle of real*real | Circle of real | Line of (real*real)list | |
= | Spline of (real*real)list; | |
datatype shape | |
= Circle of real | |
| Line of (real * real) list | |
| Rectangle of real * real | |
| Spline of (real * real) list | |
- Circle (1.2); Circle (3.4); | |
val it = Circle 1.2 : shape | |
val it = Circle 3.4 : shape | |
- Rectangle (4.3, 1.9); Rectangle (40.3, ~8.345); | |
val it = Rectangle (4.3,1.9) : shape | |
val it = Rectangle (40.3,~8.345) : shape | |
- Line [(1.1, 2.2)]; Line []; | |
val it = Line [(1.1,2.2)] : shape | |
val it = Line [] : shape | |
(* End *) | |
(* Larger functions are usually broken into several lines for readability *) | |
fun thermometer temp = | |
if temp < 37 | |
then "Cold" | |
else if temp > 37 | |
then "Warm" | |
else "Normal" | |
val test_thermo = thermometer 40 (* gives "Warm" *) | |
(* if-sentences are actually expressions and not statements/declarations. | |
A function body can only contain one expression. There are some tricks | |
for making a function do more than just one thing, though. *) | |
(* A function cannot change the variables it can refer to. It can only | |
temporarily shadow them with new variables that have the same names. In this | |
sense, variables are really constants and only behave like variables when | |
dealing with recursion. For this reason, variables are also called value | |
bindings. An example of this: *) | |
val x = 42 | |
fun answer(question) = | |
if question = "What is the meaning of life, the universe and everything?" | |
then x | |
else raise Fail "I'm an exception. Also, I don't know what the answer is." | |
val x = 43 | |
val hmm = answer "What is the meaning of life, the universe and everything?" | |
(* Now, hmm has the value 42. This is because the function answer refers to | |
the copy of x that was visible before its own function definition. *) | |
fun first_elem (x::xs) = x | |
fun second_elem (x::y::xs) = y | |
fun evenly_positioned_elems (odd::even::xs) = even::evenly_positioned_elems xs | |
| evenly_positioned_elems [odd] = [] (* Base case: throw away *) | |
| evenly_positioned_elems [] = [] (* Base case *) | |
(* The case expression can also be used to pattern match and return a value *) | |
datatype temp = | |
C of real | |
| F of real | |
fun temp_to_f t = | |
case t of | |
C x => x * (9.0 / 5.0) + 32.0 | |
| F x => x | |
(* When matching on records, you must use their slot names, and you must bind | |
every slot in a record. The order of the slots doesn't matter though. *) | |
fun rgbToTup {r, g, b} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *) | |
fun mixRgbToTup {g, b, r} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *) | |
(* If called with {r=0.1, g=0.2, b=0.3}, either of the above functions | |
would return (0.1, 0.2, 0.3). But it would be a type error to call them | |
with {r=0.1, g=0.2, b=0.3, a=0.4} *) | |
(* Higher order functions: Functions can take other functions as arguments. | |
Functions are just other kinds of values, and functions don't need names | |
to exist. Functions without names are called "anonymous functions" or | |
lambda expressions or closures (since they also have a lexical scope). *) | |
(* Here is a higher-order function that works on lists (a list combinator) *) | |
(* map f l | |
applies f to each element of l from left to right, | |
returning the list of results. *) | |
val readings = [ 34, 39, 37, 38, 35, 36, 37, 37, 37 ] (* first an int list *) | |
val opinions = List.map thermometer readings (* gives [ "Cold", "Warm", ... ] *) | |
(* And here is another one for filtering lists *) | |
val warm_readings = List.filter is_large readings (* gives [39, 38] *) | |
(* You can create your own higher-order functions, too. Functions can also take | |
several arguments by "currying" them. Syntax-wise this means adding spaces | |
between function arguments instead of commas and surrounding parentheses. *) | |
fun map f [] = [] (* We could have replaced [] by nil *) | |
| map f (x::xs) = f(x) :: map f xs | |
(* map has type ('a -> 'b) -> 'a list -> 'b list and is called polymorphic. This kind of polymorphism is called | |
parametric polymorphism in the sense that map is a generic list maping | |
function that works no matter what the types 'a and 'b are. *) | |
(* 'a is called a type variable. *) | |
(* We can declare functions as infix *) | |
val plus = add_them (* plus is now equal to the same function as add_them *) | |
infix plus (* plus is now an infix operator *) | |
val seven = 2 plus 5 (* seven is now bound to 7 *) | |
(* Functions can also be made infix before they are declared *) | |
infix minus | |
fun x minus y = x - y (* It becomes a little hard to see what's the argument *) | |
val four = 8 minus 4 (* four is now bound to 4 *) | |
(* An infix function/operator can be made prefix with 'op' *) | |
val n = op + (5, 5) (* n is now 10 *) | |
(* 'op' is useful when combined with high order functions because they expect | |
functions and not operators as arguments. Most operators are really just | |
infix functions. *) | |
(* foldl f init [x1, x2, ..., xn] | |
returns | |
f(xn, ...f(x2, f(x1, init))...) | |
or init if the list is empty. *) | |
fun fold f init [] = init | |
| fold f init (x :: xs) = f (x, fold f init xs) | |
(* if we wanted an order from left to right, then *) | |
fun fold f init [] = init | |
| fold f init (x :: xs) = fold f (f (x, init)) xs; (* uncurried function *) | |
(* if fold f a [b0, b1 , b2, b3 ...] = f (f a b0) b1 .... then we have *) | |
fun fold f init [] = init | |
| fold f init (x :: xs) = fold f (f x init) xs; (* curried function *) | |
val sum_of_numbers = foldl op+ 0 [1, 2, 3, 4, 5] | |
(* val sum_of_numbers = 15 : int *) | |
(* Here is a binary tree datatype *) | |
datatype 'a tree = Nil | |
| Node of 'a tree * 'a * 'a tree; | |
fun inorder (Nil) = [] | |
| inorder (Node (left, x, right)) = inorder (left) @ (x :: nil) @ inorder (right); | |
fun rotate (Nil) = Nil | |
| rotate (Node (Nil, x, t)) = Node (Nil, x, t) | |
| rotate (Node (Node (t1, b, t2), a, t3)) = Node (t1, b, Node (t2, a, t3)); | |
The option type is a useful datatype defined in standard library that | |
is used to signal optional result. They capture a lot of logic that | |
involves the use of the unsafe null values (null pointer in C, null in | |
Java). They are defined as | |
datatype 'a option = SOME of 'a | NONE | |
We use this to define the head and tail of a list. | |
fun head [] = NONE | |
| head (x::_) = SOME x | |
fun tail [] = NONE | |
| tail (_ :: xs) = SOME xs | |
=====================*************************========================= | |
=================-- Signatures, Structures, Functors --================== | |
A structure is defined as follows: | |
structure Name = struct | |
Definitions | |
end | |
Example: | |
structure foo = struct | |
type t = string | |
val say = fn x => TextIO.print x | |
end | |
foo.say "Hi!\n"; (* Will print Hi! *) | |
Signatures Give the interface of a structure. | |
signature NAME = sig | |
DECLARATIONS | |
end | |
Signatures, example | |
signature bar = sig | |
type t | |
val say : t -> unit | |
end | |
To say that the structure foo implements the signature bar: | |
structure foo:bar = struct | |
type t = string | |
val say = fn x => TextIO.print x | |
val hello = fn () | |
=> TextIO.print "hello/n" | |
end | |
foo.say "hej" | |
is OK, | |
but not foo.hello() as it is not the part of the signature | |
Functors | |
Build structures from structures. | |
functor FunctorName (ParamName : SigName) | |
= struct | |
... | |
end; | |
To make a structure, we do as follows: | |
structure ResultStructure | |
= FunctorName (ArgumentStructure) | |
see ordering2.sml. (mail me if you need this file) | |
The following are available (among other) | |
open_in string -> instream Create an input stream | |
input_line instream -> string Get one line | |
end_of_stream instream -> boolean Check for more | |
can_input instream -> int Check for more | |
input instream * int -> string Get loads | |
close_in instream -> unit | |
Handling input & output efficiently can be tricky. So don't bother, use one of the following to load an entire file into either a single string or a list of strings - let the operating system do the work. | |
fun fileToString fileName = let | |
val stream = open_in fileName | |
val str = input(stream, can_input stream) | |
val _ = close_in stream | |
in str end; | |
fun fileToList fileName = let | |
fun f fh = if end_of_stream fh then | |
let val _ = close_in fh in nil end | |
else (input_line fh)::(f fh) | |
in f(open_in fileName) end; | |
(* Exceptions *) | |
exception constructorName of arguments | |
(* The "of arguments" clause is optional *) | |
- exception Foo of int; | |
exception Foo of int | |
- Foo; | |
val it = fn : int -> exn | |
------------------------------END--------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment