Deep, Clear Explanations + Interactive Quiz
Introduces the foundations of Standard ML (SML), a functional programming language. Unlike Python or Java, which follow a step‑by‑step sequence of commands, SML focuses on:
Everything in SML is an expression that produces a value. There are no “do this then that” commands — only expressions that evaluate.
3 + 4
true andalso false
"Hello" ^ " World"
SML is strongly typed and statically typed. This means:
Common types:
int – whole numbersreal – decimalsbool – true/falsestring – textunit – written as (), used when no value is neededFunctions are the heart of SML. They take inputs and return outputs — no side effects.
fun square x = x * x
Let-expressions in Standard ML (SML) create temporary variables inside a block. These variables exist only within the block and disappear afterward.
let
val x = 5
in
x * x
end
Explanation:
val x = 5 creates a local binding named x that exists only inside the let ... in ... end block.x * x uses that local binding.end, x disappears — it does not affect anything outside.x is bound to 5, it cannot change.let ... in ... end produces a value.
SML does not use statements like Python. Everything is an expression that evaluates to a value.
A let-expression is SML’s way of creating local bindings for temporary variables inside a block.
Tuples can hold different types. Lists must hold the same type.
(1, "hi", true)
[1, 2, 3]
Pattern matching lets you “take apart” data structures and work with their components directly.
fun head (x::xs) = x
Explanation:
(x::xs) matches any non-empty list by splitting it into two parts:
x — the first element (the head)xs — the remaining elements (the tail)head [1,2,3]:
x = 1xs = [2,3][1,2,3] is shorthand for 1 :: (2 :: (3 :: [])).(x :: xs) matches [10,20,30] → x = 10, xs = [20,30](x :: xs) matches ["a","b"] → x = "a", xs = ["b"](x :: xs) matches [99] → x = 99, xs = []
Pattern matching only fails on an empty list [], which is why functions often include a second pattern:
fun head [] = raise Empty
| head (x::xs) = x
Pattern matching is a core feature of SML’s functional style. It allows you to handle data structures directly in function definitions without loops or conditional statements.
Introducing the most important tools in functional programming:
Functional languages avoid loops. Instead, they use recursion — a function calling itself.
fun fact 0 = 1
| fact n = n * fact (n - 1)
Pattern matching is used heavily with lists. It allows you to match different shapes of data and handle each case appropriately.
fun length [] = 0
| length (_::xs) = 1 + length xs
Explanation:
[] matches the empty list. When the input list has no elements, the length is 0.
_ :: xs matches any non-empty list.
_ means “I don’t care about the first element”.xs is the rest of the list (the tail).
For the list [1,2,3]:
length [1,2,3]
→ 1 + length [2,3]
→ 1 + (1 + length [3])
→ 1 + (1 + (1 + length []))
→ 1 + (1 + (1 + 0))
→ 3
Each step removes one element using the (_ :: xs) pattern until the list becomes empty.
length [] = 0
length (_ :: xs) = 1 + length xs
_ means “I don’t care what the first item is”.A higher‑order function is any function that does at least one of the following:
map is the classic example.
In short, higher‑order functions are functions that take other functions as inputs.
map (fn x => x * 2) [1,2,3]
How map works:
map has two inputs: a function and a list.(fn x => x * 2) is an anonymous function that doubles a number.[1,2,3] is the input to process.map applies the function to every element and returns a new list: [2,4,6].Python treats functions as values, so you can pass them into other functions just like numbers or strings. This mirrors the SML concept.
# Example — A simple higher-order function
def apply_twice(func, value):
return func(func(value))
def add_one(x):
return x + 1
print(apply_twice(add_one, 5)) # 7
Why this is higher‑order: apply_twice takes a function (func) as an argument and calls it twice on a value.
They come right after recursion and pattern matching in functional programming because they are the next key building block.
Higher-order functions solve a common problem: you often want to perform the same kind of operation but with different rules. Examples include:
Instead of rewriting the same loop every time, you write one general function and pass the specific rule into it.
Some functions work on any type.
For example, length works on:
int liststring listbool listOption types in SML help prevent errors like “head of empty list” by explicitly representing the possibility of a missing value.
fun safeHead [] = NONE
| safeHead (x::xs) = SOME x
Explanation:
NONE.SOME x.
# Python might crash
[].pop(0) # IndexError
# Or return None in some cases, but Python doesn't force you to check
SML forces you to handle the possibility of NONE, because the type system requires it. This makes programs safer.
Some operations are unsafe because the value you want might not exist. Examples include:
In many languages, these situations cause runtime errors (crashes). SML avoids this by using the Option type:
SOME x → a real value existsNONE → no value existsThis forces you to handle the “missing value” case safely, making your code more robust.