From 6e6d88e25f66e7beccafa4a1c3cef739f1edac95 Mon Sep 17 00:00:00 2001 From: Simon Shine Date: Mon, 2 Dec 2013 13:08:28 +0100 Subject: [standard-ml/en-en] Created tutorial from scratch --- standard-ml.html.markdown | 334 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 334 insertions(+) create mode 100644 standard-ml.html.markdown (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown new file mode 100644 index 00000000..d1a8fc12 --- /dev/null +++ b/standard-ml.html.markdown @@ -0,0 +1,334 @@ +--- +language: Standard ML +contributors: + - ["Simon Shine", "http://shine.eu.org/"] +lang: en-en +--- + +Standard ML is a functional programming language with type inference and some +side-effects. Some of the hard parts of learning Standard ML are: Recursion, +pattern matching, type inference (guessing the right types but never allowing +implicit type conversion). If you have an imperative background, not being able +to update variables can feel severely inhibiting. + +``` +(* Comments in Standard ML begin with (* and end with *). Comments can be + nested which means that all (* tags must end with a *) tag. This comment + contains two nested comments. *) + +(* A Standard ML program consists of declarations, e.g. value declarations: *) +val rent = 1200 +val phone_no = 5551337 +val pi = 3.14159 +val negative_number = ~15 (* Yeah, unary minus is a so-called 'tilde' *) + +(* And just as importantly, functions: *) +fun is_large(x : int) = if x > 37 then true else false + +(* Floating-point numbers are called "reals". *) +val tau = 2.0 * pi (* You can multiply reals *) +val twice_rent = 2 * rent (* You can multiply ints *) +(* val meh = 1.25 * 10 *) (* But you can't multiply an int and a real *) + +(* +, - 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" *) + +(* There are also booleans and boolean operators *) +val got_milk = true +val got_bread = false +val has_breakfast = got_milk andalso got_bread (* Yes, it's called andalso *) +val has_something = got_milk orelse got_bread (* Yes, it's called orelse *) +val is_sad = not(has_something) (* not is a function *) + +(* 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 *) + +(* The operator <> is what most other languages call != *) + + +(* 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 *) + +(* Parens 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 *) + +(* You can have lists of any kind *) +val numbers = [1, 3, 3, 7, 229, 230, 248] (* : int list *) +val names = [ "Fred", "Jane", "Alice" ] (* : string list *) +val groups = [ [ "Alice", "Bob" ], + [ "Huey", "Dewey", "Louie" ], + [ "Bonnie", "Clyde" ] ] (* : string list list *) + +val number_count = List.length numbers (* gives 7 *) + +(* You can put single values in front of lists of the same kind *) +val more_numbers = 13 :: numbers (* gives [13, 1, 3, 3, 7, ...] *) +val more_groups = ["Batman","Superman"] :: groups + +(* Lists of the same kind can be appended using the @ ("append") operator *) +val guest_list = [ "Mom", "Dad" ] @ [ "Aunt", "Uncle" ] + +(* This could have been done with the :: operator (pronounced "cons") *) +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", ... ] *) + +(* A list can contain any (finite) amount of values *) +val lots = [ 5, 5, 5, 6, 4, 5, 6, 5, 4, 5, 7, 3 ] (* still just an int list *) + +(* Lists can only contain one kind of thing... *) +(* val bad_list = [ 1, "Hello", 3.14159 ] : ??? list *) + + +(* Tuples, on the other hand, can contain a fixed number of different things *) +val person1 = ("Simon", 28, 3.14159) (* : string * int * real *) + +(* You can even have tuples inside lists and lists inside tuples *) +val likes = [ ("Alice", "ice cream"), + ("Bob", "hot dogs"), + ("Bob", "Alice") ] (* : (string * string) list *) + +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 *) + + +(* Functions! *) +fun add_them (a, b) = a + b (* A simple function that adds two numbers *) +val test_it = add_them (3, 4) (* gives 7 *) + +(* 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 can call itself as part of its result (recursion!) *) +fun fibonacci n = + if n = 0 then 0 else (* Base case *) + if n = 1 then 1 else (* Base case *) + fibonacci (n - 1) + fibonacci (n - 2) (* Recursive case *) + +(* Sometimes recursion is best understood by evaluating a function by hand: + + fibonacci 4 + ~> fibonacci (4 - 1) + fibonacci (4 - 2) + ~> fibonacci 3 + fibonacci 2 + ~> (fibonacci (3 - 1) + fibonacci (3 - 2)) + fibonacci 2 + ~> (fibonacci 2 + fibonacci 1) + fibonacci 2 + ~> ((fibonacci (2 - 1) + fibonacci (2 - 2)) + fibonacci 1) + fibonacci 2 + ~> ((fibonacci 1 + fibonacci 0) + fibonacci 1) + fibonacci 2 + ~> ((1 + fibonacci 0) + fibonacci 1) + fibonacci 2 + ~> ((1 + 0) + fibonacci 1) + fibonacci 2 + ~> (1 + fibonacci 1) + fibonacci 2 + ~> (1 + 1) + fibonacci 2 + ~> 2 + fibonacci 2 + ~> 2 + (fibonacci (2 - 1) + fibonacci (2 - 2)) + ~> 2 + (fibonacci (2 - 1) + fibonacci (2 - 2)) + ~> 2 + (fibonacci 1 + fibonacci 0) + ~> 2 + (1 + fibonacci 0) + ~> 2 + (1 + 0) + ~> 2 + 1 + ~> 3 which is the 4th Fibonacci number, according to this definition + + *) + +(* 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. *) + + +(* Functions can take several arguments by taking one tuples as argument: *) +fun solve2 (a : real, b : real, c : real) = + ( Math.sqrt (~b + Math.sqrt(b * b - 4.0*a*c) / (2.0 * a), + Math.sqrt (~b - Math.sqrt(b * b - 4.0*a*c) / (2.0 * a) ) + +(* Sometimes, the same computation is carried out several times. It makes sense + to save and re-use the result the first time. We can use "let-bindings": *) +fun solve2 (a : real, b : real, c : real) = + let val discr = b * b - 4.0*a*c + val sqr = Math.sqrt d + val denom = 2.0 * a + in (~b + sq / denom, + ~b - sq / denom) end + + +(* Pattern matching is a funky part of functional programming. It is an + alternative to if-sentences. The fibonacci function can be rewritten: *) +fun fibonacci 0 = 0 (* Base case *) + | fibonacci 1 = 1 (* Base case *) + | fibonacci n = fibonacci (n - 1) + fibonacci (n - 2) (* Recursive case *) + +(* Pattern matching is also possible on composite types like tuples and lists. + 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). *) +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 *) + + +(* 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). *) +val is_large = (fn x => x > 37) +val add_them = fn (a,b) => a + b +val thermometer = + fn temp => if temp < 37 + then "Cold" + else if temp > 37 + then "Warm" + else "Normal" + +(* The following uses an anonymous function directly and gives "ColdWarm" *) +val some_result = (fn x => thermometer (x - 5) ^ thermometer (x + 5)) 37 + +(* Here is a higher-order function that works on lists (a list combinator) *) +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 [] = [] + | map f (x::xs) = f(x) :: map f xs + +(* map has type ('a -> 'b) -> 'a list -> 'b list and is called polymorphic. *) +(* 'a is called a type variable. *) + +(* Datatypes are useful for creating both simple and complex structures *) +datatype color = Red | Green | Blue + +(* 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" + +(* 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!" + | _ = raise Fail "Unknown color" + + +(* Here is a binary tree datatype *) +datatype 'a btree = Leaf of 'a + | Node of 'a btree * 'a * 'a btree (* three-arg constructor *) + +(* Here is a binary tree *) +val myTree = Node (Leaf 9, 8, Node (Leaf 3, 5, Leaf 7)) + +(* Drawing it, it might look something like... + + 8 + / \ + leaf -> 9 5 + / \ + leaf -> 3 7 <- leaf + *) + +(* This function counts the sum of all the elements in a tree *) +fun count (Leaf n) = n + | count (Node (leftTree, n, rightTree)) = count leftTree + n + count rightTree + + +(* File I/O! *) +(* Write a nice poem to a file *) +fun writePoem(filename) = + let val file = TextIO.openOut(filename) + val _ = TextIO.output(file, "Roses are red,\nViolets are blue.\n") + val _ = TextIO.output(file, "I have a gun.\nGet in the van.\n") + in TextIO.closeOut(file) end + +(* Read a nice poem from a file into a list of strings *) +fun readPoem(filename) = + let val file = TextIO.openIn filename + val poem = TextIO.inputAll file + val _ = TextIO.closeIn file + in String.tokens (fn c => c = #"\n") poem + end + +val _ = writePoem "roses.txt" +val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", + "Violets are blue.", + "I have a gun.", + "Get in the van." ] *) +~~~~ + +## Further learning + +* Install an interactive compiler (REPL), for example + [http://www.polyml.org/](Poly/ML), + [http://mosml.org](Moscow ML) or + [http://smlnj.org/](SML/NJ). +* Follow the Coursera course [Programming Languages](https://www.coursera.org/course/proglang). +* Get the book *ML for the Working Programmer* by Larry C. Paulson. + -- cgit v1.2.3 From 9b43a17fb5cf88a31419d20fbb34f84749317b13 Mon Sep 17 00:00:00 2001 From: Simon Shine Date: Mon, 2 Dec 2013 13:12:39 +0100 Subject: [standard-ml/en-en] SML syntax highlighting on GitHub --- standard-ml.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index d1a8fc12..98c2ea39 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -11,7 +11,7 @@ pattern matching, type inference (guessing the right types but never allowing implicit type conversion). If you have an imperative background, not being able to update variables can feel severely inhibiting. -``` +```sml (* Comments in Standard ML begin with (* and end with *). Comments can be nested which means that all (* tags must end with a *) tag. This comment contains two nested comments. *) -- cgit v1.2.3 From 02b8d51897502dab5253176162d996ca05ea9cdd Mon Sep 17 00:00:00 2001 From: Simon Shine Date: Mon, 2 Dec 2013 13:21:00 +0100 Subject: [standard-ml/en-en] Silly reversal of Markdown URL tags --- standard-ml.html.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 98c2ea39..b586efa5 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -326,9 +326,9 @@ val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", ## Further learning * Install an interactive compiler (REPL), for example - [http://www.polyml.org/](Poly/ML), - [http://mosml.org](Moscow ML) or - [http://smlnj.org/](SML/NJ). + [Poly/ML](http://www.polyml.org/), + [Moscow ML](http://mosml.org) + [SML/NJ](http://smlnj.org/). * Follow the Coursera course [Programming Languages](https://www.coursera.org/course/proglang). * Get the book *ML for the Working Programmer* by Larry C. Paulson. -- cgit v1.2.3 From 699dc938e978f4b193c8d2967242386d1df61ac3 Mon Sep 17 00:00:00 2001 From: Simon Shine Date: Tue, 3 Dec 2013 07:27:29 +0100 Subject: [standard-ml/en-en] Removing syntax errors and fixing solve2 function --- standard-ml.html.markdown | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index b586efa5..ab5e4b84 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -199,17 +199,17 @@ val hmm = answer "What is the meaning of life, the universe and everything?" (* Functions can take several arguments by taking one tuples as argument: *) fun solve2 (a : real, b : real, c : real) = - ( Math.sqrt (~b + Math.sqrt(b * b - 4.0*a*c) / (2.0 * a), - Math.sqrt (~b - Math.sqrt(b * b - 4.0*a*c) / (2.0 * a) ) + ( (~b + Math.sqrt(b * b - 4.0*a*c)) / (2.0 * a), + (~b - Math.sqrt(b * b - 4.0*a*c)) / (2.0 * a) ) (* Sometimes, the same computation is carried out several times. It makes sense to save and re-use the result the first time. We can use "let-bindings": *) fun solve2 (a : real, b : real, c : real) = let val discr = b * b - 4.0*a*c - val sqr = Math.sqrt d + val sqr = Math.sqrt discr val denom = 2.0 * a - in (~b + sq / denom, - ~b - sq / denom) end + in ((~b + sqr) / denom, + (~b - sqr) / denom) end (* Pattern matching is a funky part of functional programming. It is an @@ -273,10 +273,10 @@ fun say(col) = raise Fail "Unknown color" (* Datatypes are very often used in combination with pattern matching *) -fun say Red = "You are red!" +fun say Red = "You are red!" | say Green = "You are green!" - | say Blue = "You are blue!" - | _ = raise Fail "Unknown color" + | say Blue = "You are blue!" + | say _ = raise Fail "Unknown color" (* Here is a binary tree datatype *) @@ -321,7 +321,7 @@ val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", "Violets are blue.", "I have a gun.", "Get in the van." ] *) -~~~~ +``` ## Further learning -- cgit v1.2.3 From e76f7cd4525e135599719af7379b29ce625ccb6f Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 11:45:43 +0100 Subject: [standard-ml/en-en] Update to list part --- standard-ml.html.markdown | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index ab5e4b84..f7526bb8 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -95,7 +95,8 @@ val groups = [ [ "Alice", "Bob" ], val number_count = List.length numbers (* gives 7 *) -(* You can put single values in front of lists of the same kind *) +(* You can put single values in front of lists of the same kind + using the :: ("cons") operator *) val more_numbers = 13 :: numbers (* gives [13, 1, 3, 3, 7, ...] *) val more_groups = ["Batman","Superman"] :: groups -- cgit v1.2.3 From ed82f1d8682ce6e5360177c216dc3c4eb7a148d0 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 11:47:48 +0100 Subject: [standard-ml/en-en] Add missing comma --- standard-ml.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index f7526bb8..730ef445 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -328,7 +328,7 @@ val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", * Install an interactive compiler (REPL), for example [Poly/ML](http://www.polyml.org/), - [Moscow ML](http://mosml.org) + [Moscow ML](http://mosml.org), [SML/NJ](http://smlnj.org/). * Follow the Coursera course [Programming Languages](https://www.coursera.org/course/proglang). * Get the book *ML for the Working Programmer* by Larry C. Paulson. -- cgit v1.2.3 From b8857e92663aab608b0afe6e2e79949908ac205b Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 11:48:02 +0100 Subject: [standard-ml/en-en] Add myself as a collaborator --- standard-ml.html.markdown | 1 + 1 file changed, 1 insertion(+) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 730ef445..b5fef910 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -2,6 +2,7 @@ language: Standard ML contributors: - ["Simon Shine", "http://shine.eu.org/"] + - ["David Pedersen", "http://lonelyproton.com/"] lang: en-en --- -- cgit v1.2.3 From 972a4bf80270a64466beb6fb882093cf7100dcc5 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 11:50:45 +0100 Subject: [standard-ml/en-en] Update list doc again --- standard-ml.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index b5fef910..e17972a0 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -104,7 +104,7 @@ val more_groups = ["Batman","Superman"] :: groups (* Lists of the same kind can be appended using the @ ("append") operator *) val guest_list = [ "Mom", "Dad" ] @ [ "Aunt", "Uncle" ] -(* This could have been done with the :: operator (pronounced "cons") *) +(* This could have been done with the "cons" operator *) val guest_list = "Mom" :: "Dad" :: [ "Aunt", "Uncle" ] (* If you have many lists of the same kind, you can concatenate them all *) -- cgit v1.2.3 From f1c96d5db0dae92e13be96a03118da2565ab3327 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 11:51:56 +0100 Subject: [standard-ml/en-en] Small whitespace fix --- standard-ml.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index e17972a0..56b6853c 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -186,7 +186,7 @@ fun fibonacci n = 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: *) + bindings. An example of this: *) val x = 42 fun answer(question) = -- cgit v1.2.3 From a03869362b5706345ecbb3783bd7f6173d12b698 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 12:33:02 +0100 Subject: [standard-ml/en-en] Exceptions --- standard-ml.html.markdown | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 56b6853c..547ca39b 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -302,6 +302,28 @@ fun count (Leaf n) = n | count (Node (leftTree, n, rightTree)) = count leftTree + n + count rightTree +(* Exceptions! *) +(* Exceptions can be raised using "raise" *) +fun raiseException msg = raise Fail msg + +(* This raises exception `Fail "hello from exception"` *) +(* val _ = raiseException "hello from exception" *) + +(* Exceptions can be caught using "handle" *) +val x = raiseException "hello" handle Fail msg => msg +(* x now has the value "hello" *) + +(* We can pattern match in "handle" to make sure + a specfic exception was raised, or grab the message *) +val y = raiseException "..." handle Fail _ => "Fail was raised" + | Domain => "Domain was raised" +(* y now has the value "Fail was raised" *) + +(* We can define our own exceptions like this *) +exception MyException +exception MyExceptionWithMessage of string + + (* File I/O! *) (* Write a nice poem to a file *) fun writePoem(filename) = -- cgit v1.2.3 From acc2dda568ac3bad9173a1cf3f114905a102d893 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 12:34:04 +0100 Subject: [standard-ml/en-en] align things a little --- standard-ml.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 547ca39b..9de31340 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -316,7 +316,7 @@ val x = raiseException "hello" handle Fail msg => msg (* We can pattern match in "handle" to make sure a specfic exception was raised, or grab the message *) val y = raiseException "..." handle Fail _ => "Fail was raised" - | Domain => "Domain was raised" + | Domain => "Domain was raised" (* y now has the value "Fail was raised" *) (* We can define our own exceptions like this *) -- cgit v1.2.3 From d71ac35d72529d3dee08d9591cd1c65cbdcccb6d Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 5 Dec 2013 12:43:13 +0100 Subject: [standard-ml/en-en] infix functions --- standard-ml.html.markdown | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 9de31340..849ba0f5 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -264,6 +264,24 @@ fun map f [] = [] (* map has type ('a -> 'b) -> 'a list -> 'b list and is called polymorphic. *) (* 'a is called a type variable. *) + +(* We can define functions as infix *) +fun plus (x, y) = x + y +infix plus +(* We can now call plus like "2 plus 5" *) + +(* Functions can also be made infix before they are defined *) +infix minus +fun x minus y = x - y + +(* 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 *) +val listSum = foldl op + 0 [1,2,3,4,5] + + (* Datatypes are useful for creating both simple and complex structures *) datatype color = Red | Green | Blue -- cgit v1.2.3 From e26c663f75d45cf8495494a51e95c8abebda3167 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 11 Dec 2013 08:40:07 -0800 Subject: Fix standard ml --- standard-ml.html.markdown | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 849ba0f5..bd26709c 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -1,9 +1,8 @@ --- -language: Standard ML +language: "Standard ML" contributors: - ["Simon Shine", "http://shine.eu.org/"] - ["David Pedersen", "http://lonelyproton.com/"] -lang: en-en --- Standard ML is a functional programming language with type inference and some @@ -12,7 +11,7 @@ pattern matching, type inference (guessing the right types but never allowing implicit type conversion). If you have an imperative background, not being able to update variables can feel severely inhibiting. -```sml +```ocaml (* Comments in Standard ML begin with (* and end with *). Comments can be nested which means that all (* tags must end with a *) tag. This comment contains two nested comments. *) -- cgit v1.2.3 From bf659f14cf55e815cae22230c100e31a96b7d1c4 Mon Sep 17 00:00:00 2001 From: Simon Shine Date: Mon, 16 Dec 2013 21:42:15 +0100 Subject: [standard-ml/en-en] Some format fixing, variable renaming, some more about exceptions --- standard-ml.html.markdown | 99 ++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 39 deletions(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index bd26709c..b545f3e1 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -13,21 +13,21 @@ to update variables can feel severely inhibiting. ```ocaml (* Comments in Standard ML begin with (* and end with *). Comments can be - nested which means that all (* tags must end with a *) tag. This comment - contains two nested comments. *) + nested which means that all (* tags must end with a *) tag. This comment, + for example, contains two nested comments. *) (* A Standard ML program consists of declarations, e.g. value declarations: *) val rent = 1200 val phone_no = 5551337 val pi = 3.14159 -val negative_number = ~15 (* Yeah, unary minus is a so-called 'tilde' *) +val negative_number = ~15 (* Yeah, unary minus uses the 'tilde' symbol *) (* And just as importantly, functions: *) fun is_large(x : int) = if x > 37 then true else false (* Floating-point numbers are called "reals". *) -val tau = 2.0 * pi (* You can multiply reals *) -val twice_rent = 2 * rent (* You can multiply ints *) +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 *) (* +, - and * are overloaded so they work for both int and real. *) @@ -42,16 +42,16 @@ val negative_rent = ~(rent) (* Would also have worked if rent were a "real" *) (* There are also booleans and boolean operators *) val got_milk = true val got_bread = false -val has_breakfast = got_milk andalso got_bread (* Yes, it's called andalso *) -val has_something = got_milk orelse got_bread (* Yes, it's called orelse *) +val has_breakfast = got_milk andalso got_bread (* 'andalso' is the operator *) +val has_something = got_milk orelse got_bread (* 'orelse' is the operator *) val is_sad = not(has_something) (* not is a function *) (* 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 *) -(* The operator <> is what most other languages call != *) - +(* The operator <> is what most other languages call !=. *) +(* 'andalso' and 'orelse' are called && and || in many other languages. *) (* Actually, most of the parentheses above are unnecessary. Here are some different ways to say some of the things mentioned above: *) @@ -61,7 +61,7 @@ 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 *) -(* Parens are mostly necessary when grouping things: *) +(* 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! *) @@ -84,32 +84,37 @@ val bar = [ #"H", #"e", #"l", #"l", #"o" ] (* SML also has lists! *) 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 *) +val _ = print (bob ^ "\n") (* For good measure, add a linebreak *) (* You can have lists of any kind *) val numbers = [1, 3, 3, 7, 229, 230, 248] (* : int list *) val names = [ "Fred", "Jane", "Alice" ] (* : string list *) + +(* Even lists of lists of things *) val groups = [ [ "Alice", "Bob" ], [ "Huey", "Dewey", "Louie" ], [ "Bonnie", "Clyde" ] ] (* : string list list *) val number_count = List.length numbers (* gives 7 *) -(* You can put single values in front of lists of the same kind - using the :: ("cons") operator *) +(* You can put single values in front of lists of the same kind using + the :: operator, called "the cons operator" (known from Lisp). *) val more_numbers = 13 :: numbers (* gives [13, 1, 3, 3, 7, ...] *) val more_groups = ["Batman","Superman"] :: groups (* Lists of the same kind can be appended using the @ ("append") operator *) val guest_list = [ "Mom", "Dad" ] @ [ "Aunt", "Uncle" ] -(* This could have been done with the "cons" operator *) +(* This could have been done with the "cons" operator. It is tricky because the + left-hand-side must be an element whereas the right-hand-side must be a list + of those elements. *) val guest_list = "Mom" :: "Dad" :: [ "Aunt", "Uncle" ] +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", ... ] *) -(* A list can contain any (finite) amount of values *) +(* A list can contain any (finite) number of values *) val lots = [ 5, 5, 5, 6, 4, 5, 6, 5, 4, 5, 7, 3 ] (* still just an int list *) (* Lists can only contain one kind of thing... *) @@ -264,21 +269,23 @@ fun map f [] = [] (* 'a is called a type variable. *) -(* We can define functions as infix *) -fun plus (x, y) = x + y -infix plus -(* We can now call plus like "2 plus 5" *) +(* 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 defined *) +(* Functions can also be made infix before they are declared *) infix minus -fun x minus y = x - y +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 *) +(* 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 *) -val listSum = foldl op + 0 [1,2,3,4,5] +(* '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. *) +val sum_of_numbers = foldl op+ 0 [1,2,3,4,5] (* Datatypes are useful for creating both simple and complex structures *) @@ -291,6 +298,8 @@ fun say(col) = if col = Blue then "You are blue!" else raise Fail "Unknown color" +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!" @@ -318,28 +327,40 @@ val myTree = Node (Leaf 9, 8, Node (Leaf 3, 5, Leaf 7)) fun count (Leaf n) = n | count (Node (leftTree, n, rightTree)) = count leftTree + n + count rightTree +val myTreeCount = count myTree (* myTreeCount is now bound to 32 *) -(* Exceptions! *) -(* Exceptions can be raised using "raise" *) -fun raiseException msg = raise Fail msg -(* This raises exception `Fail "hello from exception"` *) -(* val _ = raiseException "hello from exception" *) +(* Exceptions! *) +(* Exceptions can be raised/thrown using the reserved word 'raise' *) +fun calculate_interest(n) = if n < 0.0 + then raise Domain + else n * 1.04 (* Exceptions can be caught using "handle" *) -val x = raiseException "hello" handle Fail msg => msg -(* x now has the value "hello" *) +val balance = calculate_interest ~180.0 + handle Domain => ~180.0 (* x now has the value ~180.0 *) -(* We can pattern match in "handle" to make sure +(* Some exceptions carry extra information with them *) +(* Here are some examples of built-in exceptions *) +fun failing_function [] = raise Empty (* used for empty lists *) + | failing_function [x] = raise Fail "This list is too short!" + | failing_function [x,y] = raise Overflow (* used for arithmetic *) + | failing_function xs = raise Fail "This list is too long!" + +(* We can pattern match in 'handle' to make sure a specfic exception was raised, or grab the message *) -val y = raiseException "..." handle Fail _ => "Fail was raised" - | Domain => "Domain was raised" -(* y now has the value "Fail was raised" *) +val err_msg = failing_function [1,2] handle Fail _ => "Fail was raised" + | Domain => "Domain was raised" + | Empty => "Empty was raised" + | _ => "Unknown exception" + +(* err_msg now has the value "Unknown exception" because Overflow isn't + listed as one of the patterns -- thus, the catch-all pattern _ is used. *) (* We can define our own exceptions like this *) exception MyException exception MyExceptionWithMessage of string - +exception SyntaxError of string * (int * int) (* File I/O! *) (* Write a nice poem to a file *) @@ -372,4 +393,4 @@ val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", [SML/NJ](http://smlnj.org/). * Follow the Coursera course [Programming Languages](https://www.coursera.org/course/proglang). * Get the book *ML for the Working Programmer* by Larry C. Paulson. - +* Use [StackOverflow's sml tag](http://stackoverflow.com/questions/tagged/sml). -- cgit v1.2.3 From 02bc5183512e1548bfb3a54bacc85443dc33f86d Mon Sep 17 00:00:00 2001 From: James Baker Date: Sun, 26 Apr 2015 09:59:15 +0100 Subject: Standard ML has references Standard ML is not a pure language and has imperative structures like references while and seq --- standard-ml.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index b545f3e1..cc5132f1 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -8,8 +8,8 @@ contributors: Standard ML is a functional programming language with type inference and some side-effects. Some of the hard parts of learning Standard ML are: Recursion, pattern matching, type inference (guessing the right types but never allowing -implicit type conversion). If you have an imperative background, not being able -to update variables can feel severely inhibiting. +implicit type conversion). Standard ML is distinguished from Haskell by including +references, allowing variables to be updated. ```ocaml (* Comments in Standard ML begin with (* and end with *). Comments can be -- cgit v1.2.3 From f8260574d9a29d5302dccf8fb883d5e3d60592d0 Mon Sep 17 00:00:00 2001 From: James Baker Date: Sun, 26 Apr 2015 10:00:21 +0100 Subject: Add examples of imperative-style control Make reference and update, add use of a while and use of seq --- standard-ml.html.markdown | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index cc5132f1..8fc849cb 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -383,6 +383,25 @@ val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,", "Violets are blue.", "I have a gun.", "Get in the van." ] *) + +(* We can create references to data which can be updated *) +val counter = ref 0 (* Produce a reference with the ref function *) + +(* Assign to a reference with the assignment operator *) +fun set_five reference = reference := 5 + +(* Read a reference with the dereference operator *) +fun equals_five reference = !reference = 5 + +(* We can use while loops for when recursion is messy *) +fun decrement_to_zero r = if !r < 0 + then r := 0 + else while !r >= 0 do r := !r - 1 + +(* This returns the unit value (in practical terms, nothing, a 0-tuple) *) + +(* To allow returning a value, we can use the semicolon to sequence evaluations *) +fun decrement_ret x y = (x := !x - 1; y) ``` ## Further learning -- cgit v1.2.3 From 183ca56459828f567457337204c7ef10351a31f0 Mon Sep 17 00:00:00 2001 From: James Baker Date: Sun, 26 Apr 2015 10:01:36 +0100 Subject: Add myself as a contributor --- standard-ml.html.markdown | 1 + 1 file changed, 1 insertion(+) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 8fc849cb..07896beb 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -3,6 +3,7 @@ language: "Standard ML" contributors: - ["Simon Shine", "http://shine.eu.org/"] - ["David Pedersen", "http://lonelyproton.com/"] + - ["James Baker", "http://www.jbaker.io/"] --- Standard ML is a functional programming language with type inference and some -- cgit v1.2.3 From 152c03ddf9072ed80efef0528968ca132385a2aa Mon Sep 17 00:00:00 2001 From: inaimathi Date: Sat, 6 Jun 2015 12:51:45 -0400 Subject: Add notes about records to Standard ML page. --- standard-ml.html.markdown | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 07896beb..125183c0 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -4,6 +4,7 @@ contributors: - ["Simon Shine", "http://shine.eu.org/"] - ["David Pedersen", "http://lonelyproton.com/"] - ["James Baker", "http://www.jbaker.io/"] + - ["Leo Zovic", "http://langnostic.inaimathi.ca/"] --- Standard ML is a functional programming language with type inference and some @@ -136,9 +137,29 @@ val mixup = [ ("Alice", 39), val good_bad_stuff = (["ice cream", "hot dogs", "chocolate"], - ["liver", "paying the rent" ]) (* string list * string list *) + ["liver", "paying the rent" ]) (* : string list * string list *) +(* Records are tuples with named slots *) + +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 *) + (* Functions! *) fun add_them (a, b) = a + b (* A simple function that adds two numbers *) val test_it = add_them (3, 4) (* gives 7 *) @@ -225,17 +246,26 @@ fun fibonacci 0 = 0 (* Base case *) | fibonacci 1 = 1 (* Base case *) | fibonacci n = fibonacci (n - 1) + fibonacci (n - 2) (* Recursive case *) -(* Pattern matching is also possible on composite types like tuples and lists. - 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). *) +(* 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). *) 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 *) +(* 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 -- cgit v1.2.3 From 21c3187f11cbb9926959af23d76743718e96d77b Mon Sep 17 00:00:00 2001 From: inaimathi Date: Sat, 6 Jun 2015 12:53:33 -0400 Subject: Fix indentation --- standard-ml.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'standard-ml.html.markdown') diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown index 125183c0..143980e7 100644 --- a/standard-ml.html.markdown +++ b/standard-ml.html.markdown @@ -4,7 +4,7 @@ contributors: - ["Simon Shine", "http://shine.eu.org/"] - ["David Pedersen", "http://lonelyproton.com/"] - ["James Baker", "http://www.jbaker.io/"] - - ["Leo Zovic", "http://langnostic.inaimathi.ca/"] + - ["Leo Zovic", "http://langnostic.inaimathi.ca/"] --- Standard ML is a functional programming language with type inference and some -- cgit v1.2.3