From 8fb078989874cfe256eb4037030c16ffe768e912 Mon Sep 17 00:00:00 2001 From: "D. H. Lorenz" Date: Thu, 16 May 2024 14:06:28 +0300 Subject: [PATCH] 236319-Spr-2023 --- HW/Homework1/q1.pas | 2 +- HW/Homework2/q1.expected | 10 + HW/Homework2/q1.in | 9 + HW/Homework2/q2.expected | 6 + HW/Homework2/q2.in | 5 + HW/Homework2/q3.expected | 4 + HW/Homework2/q3.in | 3 + HW/Homework3/hw3_q1_def.sml | 1 + HW/Homework3/hw3_q2_def.sml | 13 + HW/Homework3/q1.expected | 9 + HW/Homework3/q1.in | 6 + HW/Homework3/q2.expected | 6 + HW/Homework3/q2.in | 20 + HW/Homework3/selfcheck.sh | 69 +++ HW/Homework4/hw4_q1_def.sml | 51 +++ HW/Homework4/hw4_q2_def.sml | 5 + HW/Homework4/hw4_q3_def.sml | 12 + HW/Homework4/q1.expected | 16 + HW/Homework4/q1.in | 6 + HW/Homework4/q2.expected | 4 + HW/Homework4/q2.in | 3 + HW/Homework4/q3.expected | 10 + HW/Homework4/q3.in | 8 + HW/Homework4/selfcheck.sh | 85 ++++ HW/Homework5/q1.expected | 12 + HW/Homework5/q1.in | 17 + HW/Homework5/q2.expected | 20 + HW/Homework5/q2.in | 38 ++ HW/Homework5/selfcheck.sh | 73 ++++ HW/Homework6/q1.expected | 6 + HW/Homework6/q1.in | 7 + HW/Homework6/q2.expected | 6 + HW/Homework6/q2.in | 27 ++ HW/Homework6/selfcheck.sh | 80 ++++ Material/Tutorials/imgs/docker.png | Bin 0 -> 47520 bytes Material/Tutorials/misc/docker.md | 149 +++++++ Material/Tutorials/pascal/overview.md | 16 +- Material/Tutorials/prolog/clp.md | 76 ++-- .../Tutorials/prolog/control-predicates.md | 6 +- Material/Tutorials/prolog/exam-questions.md | 53 --- Material/Tutorials/prolog/introduction.md | 267 +++++++----- Material/Tutorials/prolog/lists.md | 165 +++++-- Material/Tutorials/sml/datatypes.md | 119 +++-- Material/Tutorials/sml/declarations.md | 171 ++++---- Material/Tutorials/sml/exam-questions.md | 2 +- Material/Tutorials/sml/exceptions.md | 65 +-- Material/Tutorials/sml/functions.md | 42 +- Material/Tutorials/sml/introduction.md | 408 +++++++++--------- Material/Tutorials/sml/lists.md | 129 ++++-- Material/Tutorials/sml/lists_solutions.md | 269 ++++++++++++ Material/Tutorials/sml/modules.md | 71 ++- Material/Tutorials/sml/refs.md | 112 ++++- Material/Tutorials/sml/sequences.md | 168 ++++++-- Material/Tutorials/theory/gradual_typing.md | 247 +++++++++++ Material/Tutorials/theory/rtti.md | 84 ++-- Material/Tutorials/theory/sml-type-system.md | 217 ---------- .../Tutorials/theory/type_constructors.md | 330 ++++++++++++++ 57 files changed, 2813 insertions(+), 1002 deletions(-) create mode 100644 HW/Homework2/q1.expected create mode 100644 HW/Homework2/q1.in create mode 100644 HW/Homework2/q2.expected create mode 100644 HW/Homework2/q2.in create mode 100644 HW/Homework2/q3.expected create mode 100644 HW/Homework2/q3.in create mode 100644 HW/Homework3/hw3_q1_def.sml create mode 100644 HW/Homework3/hw3_q2_def.sml create mode 100644 HW/Homework3/q1.expected create mode 100644 HW/Homework3/q1.in create mode 100644 HW/Homework3/q2.expected create mode 100644 HW/Homework3/q2.in create mode 100644 HW/Homework3/selfcheck.sh create mode 100644 HW/Homework4/hw4_q1_def.sml create mode 100644 HW/Homework4/hw4_q2_def.sml create mode 100644 HW/Homework4/hw4_q3_def.sml create mode 100644 HW/Homework4/q1.expected create mode 100644 HW/Homework4/q1.in create mode 100644 HW/Homework4/q2.expected create mode 100644 HW/Homework4/q2.in create mode 100644 HW/Homework4/q3.expected create mode 100644 HW/Homework4/q3.in create mode 100644 HW/Homework4/selfcheck.sh create mode 100644 HW/Homework5/q1.expected create mode 100644 HW/Homework5/q1.in create mode 100644 HW/Homework5/q2.expected create mode 100644 HW/Homework5/q2.in create mode 100644 HW/Homework5/selfcheck.sh create mode 100644 HW/Homework6/q1.expected create mode 100644 HW/Homework6/q1.in create mode 100644 HW/Homework6/q2.expected create mode 100644 HW/Homework6/q2.in create mode 100644 HW/Homework6/selfcheck.sh create mode 100644 Material/Tutorials/imgs/docker.png create mode 100644 Material/Tutorials/misc/docker.md create mode 100644 Material/Tutorials/sml/lists_solutions.md create mode 100644 Material/Tutorials/theory/gradual_typing.md delete mode 100644 Material/Tutorials/theory/sml-type-system.md create mode 100644 Material/Tutorials/theory/type_constructors.md diff --git a/HW/Homework1/q1.pas b/HW/Homework1/q1.pas index 39f2655..5ce6228 100644 --- a/HW/Homework1/q1.pas +++ b/HW/Homework1/q1.pas @@ -15,5 +15,5 @@ { Construct the new line and then print it. } - end; + end end. diff --git a/HW/Homework2/q1.expected b/HW/Homework2/q1.expected new file mode 100644 index 0000000..229f79d --- /dev/null +++ b/HW/Homework2/q1.expected @@ -0,0 +1,10 @@ +val it = () : unit +val it = fn : 'a -> 'b -> ('a * 'b -> 'b) -> 'b +val it = fn : int * real -> (real -> string) -> bool +val it = fn : ('a -> 'b -> 'c) -> 'a -> 'b -> 'd -> 'c +val it = fn : 'a -> 'b -> int -> int -> int +val it = fn : ('a -> 'b) -> 'a -> ('b * 'b -> 'c) -> 'c +val it = fn : unit -> unit -> int +val it = fn : 'a -> 'a * 'a -> 'a +val it = fn : int * string * string -> int * string * string +- diff --git a/HW/Homework2/q1.in b/HW/Homework2/q1.in new file mode 100644 index 0000000..0d02d6f --- /dev/null +++ b/HW/Homework2/q1.in @@ -0,0 +1,9 @@ +print "===TEST START===\n"; +sig1; +sig2; +sig3; +sig4; +sig5; +sig6; +sig7; +sig8; diff --git a/HW/Homework2/q2.expected b/HW/Homework2/q2.expected new file mode 100644 index 0000000..51c398b --- /dev/null +++ b/HW/Homework2/q2.expected @@ -0,0 +1,6 @@ +val it = () : unit +val it = fn : int -> int -> int +val test_1 = "Passed" : string +val it = fn : int * int -> int +val test_2 = "Passed" : string +- diff --git a/HW/Homework2/q2.in b/HW/Homework2/q2.in new file mode 100644 index 0000000..1e4328b --- /dev/null +++ b/HW/Homework2/q2.in @@ -0,0 +1,5 @@ +print "===TEST START===\n"; +curry op*; +val test_1 = if 12 = it 3 4 then "Passed" else "Failed"; +uncurry it; +val test_2 = if 12 = it (3,4) then "Passed" else "Failed"; diff --git a/HW/Homework2/q3.expected b/HW/Homework2/q3.expected new file mode 100644 index 0000000..220cc34 --- /dev/null +++ b/HW/Homework2/q3.expected @@ -0,0 +1,4 @@ +val it = () : unit +val test_1 = "Passed" : string +val test_2 = "Passed" : string +- diff --git a/HW/Homework2/q3.in b/HW/Homework2/q3.in new file mode 100644 index 0000000..cfb73e4 --- /dev/null +++ b/HW/Homework2/q3.in @@ -0,0 +1,3 @@ +print "===TEST START===\n"; +val test_1 = if alive = is_alive (alive, empty, empty) (empty, alive, alive) (empty, empty, empty) then "Passed" else "Failed"; +val test_2 = if empty = is_alive (alive, alive, alive) (empty, alive, empty) (alive, alive, alive) then "Passed" else "Failed"; diff --git a/HW/Homework3/hw3_q1_def.sml b/HW/Homework3/hw3_q1_def.sml new file mode 100644 index 0000000..deba66f --- /dev/null +++ b/HW/Homework3/hw3_q1_def.sml @@ -0,0 +1 @@ +fun toChar false = #" " | toChar true = #"*" \ No newline at end of file diff --git a/HW/Homework3/hw3_q2_def.sml b/HW/Homework3/hw3_q2_def.sml new file mode 100644 index 0000000..7f53c72 --- /dev/null +++ b/HW/Homework3/hw3_q2_def.sml @@ -0,0 +1,13 @@ +signature KERNEL1D_SIG = sig + type source + type target + val kernel : source -> source -> source -> target + val default : source -> source +end; + +signature KERNEL2D_SIG = sig + type source + type target + val kernel : source * source * source -> source * source * source -> source * source * source -> target + val default : source -> source +end; \ No newline at end of file diff --git a/HW/Homework3/q1.expected b/HW/Homework3/q1.expected new file mode 100644 index 0000000..0779454 --- /dev/null +++ b/HW/Homework3/q1.expected @@ -0,0 +1,9 @@ +val it = () : unit +val test1 = "PASSED" : string +val test2 = "PASSED" : string +val test3 = "PASSED" : string +val test4 = "PASSED" : string + * +* * +val test5 = () : unit + diff --git a/HW/Homework3/q1.in b/HW/Homework3/q1.in new file mode 100644 index 0000000..55f7328 --- /dev/null +++ b/HW/Homework3/q1.in @@ -0,0 +1,6 @@ +print "===TEST START===\n"; +val test1 = if (mapState (fn true => 1 | false => 0) [[false, true, false], [true, false, true]]) = [[0, 1, 0], [1, 0, 1]] then "PASSED" else "FAILED"; +val test2 = if (toString (explode "Hello world")) = "Hello world" then "PASSED" else "FAILED"; +val test3 = if (frameToState [" * ", "* *"]) = [[false, true, false], [true, false, true]] then "PASSED" else "FAILED"; +val test4 = if (stateToFrame [[false, true, false], [true, false, true]]) = [" * ", "* *"]then "PASSED" else "FAILED"; +val test5 = printFrame [" * ", "* *"]; \ No newline at end of file diff --git a/HW/Homework3/q2.expected b/HW/Homework3/q2.expected new file mode 100644 index 0000000..66fe70b --- /dev/null +++ b/HW/Homework3/q2.expected @@ -0,0 +1,6 @@ +val it = () : unit +val test1 = "PASSED" : string +val test2 = "PASSED" : string +val test3 = "PASSED" : string +val test4 = "PASSED" : string + diff --git a/HW/Homework3/q2.in b/HW/Homework3/q2.in new file mode 100644 index 0000000..00b84f3 --- /dev/null +++ b/HW/Homework3/q2.in @@ -0,0 +1,20 @@ +fun sum a b c = a + b + c; + +structure SumKernel1D = Kernel1D(struct + type source = int + type target = int + val kernel = sum + fun default _ = 0 +end); +structure SumKernel2D = Kernel2D(struct + type source = int + type target = int + fun kernel (x1, x2, x3) (y1, y2, y3) (z1, z2, z3) = 2 * x2 + y2 + 3 * z2 + fun default _ = 0 +end); + +print "===TEST START===\n"; +val test1 = if (SumKernel1D.runKernel [1, 2, 3, 4]) = [3, 6, 9, 7] then "PASSED" else "FAILED"; +val test2 = if (zip [1, 2, 3] [4, 5, 6] [7, 8, 9]) = [(1, 4, 7), (2, 5, 8), (3, 6, 9)] then "PASSED" else "FAILED"; +val test3 = if (fill false 3) = [false, false, false] then "PASSED" else "FAILED"; +val test4 = if (SumKernel2D.runKernel [[1, 2, 3], [4, 5, 6], [7, 8, 9]]) = [[7, 13, 7], [19, 31, 16], [31, 49, 25]] then "PASSED" else "FAILED"; \ No newline at end of file diff --git a/HW/Homework3/selfcheck.sh b/HW/Homework3/selfcheck.sh new file mode 100644 index 0000000..6158f7a --- /dev/null +++ b/HW/Homework3/selfcheck.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +testsurl="https://raw.githubusercontent.com/AdiHarif/236319-Spr-2023/master/HW/Homework3/" + +tmpdir="selfcheck_tmp" +test_files=("q1" "q2") +required_files=("hw3_q1.sml" "hw3_q2.sml" "dry.pdf") + +if [ -z "$1" ]; then + echo "Usage: ./"$( basename "$0" )" " + exit +fi + +if [ ! -f "$1" ]; then + echo "Submission zip file not found!" + exit +fi + +rm -rf "$tmpdir" &> /dev/null +if [ -d "$tmpdir" ] + then + echo "Cannot clear tmp directory. Please delete '$tmpdir' manually and try again" + exit +fi +mkdir "$tmpdir" &> /dev/null + +yes | apt install zip &> /dev/null + +unzip "$1" -d "$tmpdir" &> /dev/null +if [[ $? != 0 ]]; then + echo "Unable to unzip submission file!" + exit +fi + +cd "$tmpdir" +for f in "${required_files[@]}" +do + if [ ! -f $f ]; then + echo "File $f not found!" + exit + fi +done + +if [ $( ls | wc -l ) != ${#required_files[@]} ]; then + echo "There are too many files in the submission" + exit +fi + +for test in "${test_files[@]}" +do + wget "$testsurl$test.in" "$testsurl$test.expected" "${testsurl}hw3_${test}_def.sml" &> /dev/null + if [ ! -f "$test.in" ] || [ ! -f "$test.expected" ] || [ ! -f "hw3_${test}_def.sml" ]; then + echo "Unable to download test $test!" + exit + fi + sml hw3_$test.sml < $test.in &> $test.rawout + sed '1,/===TEST START===/d' $test.rawout > $test.out + diff $test.out $test.expected + if [[ $? != 0 ]]; then + echo "Failed test $test!" + exit + fi +done + +cd - &> /dev/null +rm -rf "$tmpdir" + +echo "Ok to submit :)" +exit diff --git a/HW/Homework4/hw4_q1_def.sml b/HW/Homework4/hw4_q1_def.sml new file mode 100644 index 0000000..cb59fc1 --- /dev/null +++ b/HW/Homework4/hw4_q1_def.sml @@ -0,0 +1,51 @@ +use "hw2_q3.sml"; +use "hw3_q1.sml"; +use "hw3_q2.sml"; + +local + fun toCell false = empty + | toCell true = alive; +in +fun is_alive_bool (x1, x2, x3) (y1, y2, y3) (z1, z2, z3) = + case ( + is_alive + (toCell x1, toCell x2, toCell x3) + (toCell y1, toCell y2, toCell y3) + (toCell z1, toCell z2, toCell z3)) of empty => false | alive => true +end; + +fun run f 0 _ = () + | run f times delay = (f(); OS.Process.system ("sleep " ^ Real.toString delay); run f (times - 1) delay); + +val start_frame = [ +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" *** *** *** *** * *** ", +" * * * * ** * * ", +" *** *** *** *** * *** ", +" * * * * * * * ", +" *** *** *** *** *** *** ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "]; \ No newline at end of file diff --git a/HW/Homework4/hw4_q2_def.sml b/HW/Homework4/hw4_q2_def.sml new file mode 100644 index 0000000..e49e5ab --- /dev/null +++ b/HW/Homework4/hw4_q2_def.sml @@ -0,0 +1,5 @@ +datatype 'a tree = + Nil + | Br of 'a * ('a tree) * ('a tree); + +datatype ('a, 'b) union = type1 of 'a | type2 of 'b; \ No newline at end of file diff --git a/HW/Homework4/hw4_q3_def.sml b/HW/Homework4/hw4_q3_def.sml new file mode 100644 index 0000000..684d190 --- /dev/null +++ b/HW/Homework4/hw4_q3_def.sml @@ -0,0 +1,12 @@ +datatype 'a seq = Nil | Cons of 'a * (unit -> 'a seq); + +fun counter () = let + val count = ref 0; + fun aux n () = Cons (n + 1, ( + count := 1 + !count; + print ("exec: " ^ Int.toString (!count) ^ "\n"); + aux (n + 1) + )) +in + (Cons(0, aux 0)) +end; diff --git a/HW/Homework4/q1.expected b/HW/Homework4/q1.expected new file mode 100644 index 0000000..a8e4cd9 --- /dev/null +++ b/HW/Homework4/q1.expected @@ -0,0 +1,16 @@ +val it = () : unit +val test1 = "PASSED" : string +val game = fn : unit -> unit + *** + * * + *** +val it = () : unit + * * +* * + * * +val it = () : unit + +** ** + +val it = () : unit + diff --git a/HW/Homework4/q1.in b/HW/Homework4/q1.in new file mode 100644 index 0000000..ad8ed7d --- /dev/null +++ b/HW/Homework4/q1.in @@ -0,0 +1,6 @@ +print "===TEST START===\n"; +val test1 = if (runCycle [" *** ", " * * ", " *** "]) = [" * * ","* *"," * * "] then "PASSED" else "FAILED"; +val game = gameOfLife [" *** ", " * * ", " *** "]; +game(); +game(); +game(); \ No newline at end of file diff --git a/HW/Homework4/q2.expected b/HW/Homework4/q2.expected new file mode 100644 index 0000000..7949f63 --- /dev/null +++ b/HW/Homework4/q2.expected @@ -0,0 +1,4 @@ +val it = () : unit +val it = [0,1,2] : int list +val it = Br (0,Br (2,Nil,Nil),Br (4,Nil,Nil)) : int tree + diff --git a/HW/Homework4/q2.in b/HW/Homework4/q2.in new file mode 100644 index 0000000..cbdb80e --- /dev/null +++ b/HW/Homework4/q2.in @@ -0,0 +1,3 @@ +print "===TEST START===\n"; +flatten (Br(0, Br(1, Nil, Nil), Br(2, Nil, Nil))); +map (fn x => 2 * x) (Br(0, Br(1, Nil, Nil), Br(2, Nil, Nil))); \ No newline at end of file diff --git a/HW/Homework4/q3.expected b/HW/Homework4/q3.expected new file mode 100644 index 0000000..93cf392 --- /dev/null +++ b/HW/Homework4/q3.expected @@ -0,0 +1,10 @@ +val it = () : unit +exec: 1 +val it = () : unit +exec: 2 +val it = () : unit +val it = () : unit +val it = () : unit +exec: 3 +val it = () : unit +- diff --git a/HW/Homework4/q3.in b/HW/Homework4/q3.in new file mode 100644 index 0000000..6668bc0 --- /dev/null +++ b/HW/Homework4/q3.in @@ -0,0 +1,8 @@ +val s = ref (new (counter ())); + +print "===TEST START===\n"; +s := next (!s); +s := next (!s); +s := prev (!s); +s := next (!s); +s := next (!s); diff --git a/HW/Homework4/selfcheck.sh b/HW/Homework4/selfcheck.sh new file mode 100644 index 0000000..c7cc5ba --- /dev/null +++ b/HW/Homework4/selfcheck.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +hw2="https://raw.githubusercontent.com/AdiHarif/236319-Spr-2023/master/HW/Homework2/" +hw3="https://raw.githubusercontent.com/AdiHarif/236319-Spr-2023/master/HW/Homework3/" +hw4="https://raw.githubusercontent.com/AdiHarif/236319-Spr-2023/master/HW/Homework4/" + +tmpdir="selfcheck_tmp" +test_files=("q1" "q2" "q3") +required_files=("hw4_q1.sml" "hw4_q2.sml" "hw4_q3.sml" "dry.pdf") + +if [ -z "$2" ]; then + echo "Usage: ./"$( basename "$0" )" " + exit +fi + +if [ ! -f "$1" ]; then + echo "Submission zip file not found!" + exit +fi + +if [ ! -d "$2" ]; then + echo "Directory with previous solutions not found!" + exit +fi + +rm -rf "$tmpdir" &> /dev/null +if [ -d "$tmpdir" ] + then + echo "Cannot clear tmp directory. Please delete '$tmpdir' manually and try again" + exit +fi +mkdir "$tmpdir" &> /dev/null + +yes | apt install zip &> /dev/null + +unzip "$1" -d "$tmpdir" &> /dev/null +if [[ $? != 0 ]]; then + echo "Unable to unzip submission file!" + exit +fi + +cd "$tmpdir" +for f in "${required_files[@]}" +do + if [ ! -f $f ]; then + echo "File $f not found!" + exit + fi +done + +if [ $( ls | wc -l ) != ${#required_files[@]} ]; then + echo "There are too many files in the submission" + exit +fi + +cp ../$2/* . &> /dev/null + +wget "${hw3}hw3_q1_def.sml" "${hw3}hw3_q2_def.sml" &> /dev/null +if [ ! -f "hw3_q1_def.sml" ] || [ ! -f "hw3_q2_def.sml" ]; then + echo "Unable to download def files from previous homeworks!" + exit +fi + +for test in "${test_files[@]}" +do + wget "$hw4$test.in" "$hw4$test.expected" "${hw4}hw4_${test}_def.sml" &> /dev/null + sleep 3 + if [ ! -f "$test.in" ] || [ ! -f "$test.expected" ] || [ ! -f "hw4_${test}_def.sml" ]; then + echo "Unable to download test $test!" + exit + fi + sml hw4_$test.sml < $test.in &> $test.rawout + sed '1,/===TEST START===/d' $test.rawout > $test.out + diff $test.out $test.expected + if [[ $? != 0 ]]; then + echo "Failed test $test!" + exit + fi +done + +cd - &> /dev/null +rm -rf "$tmpdir" + +echo "Ok to submit :)" +exit diff --git a/HW/Homework5/q1.expected b/HW/Homework5/q1.expected new file mode 100644 index 0000000..b0d7f6c --- /dev/null +++ b/HW/Homework5/q1.expected @@ -0,0 +1,12 @@ + +true. + +false. + +false. + +true. + +L = [[3, 17], [7, 13], [13, 7], [17, 3]]. + + diff --git a/HW/Homework5/q1.in b/HW/Homework5/q1.in new file mode 100644 index 0000000..0e0e595 --- /dev/null +++ b/HW/Homework5/q1.in @@ -0,0 +1,17 @@ +use_module(library(yall)). +print("===TEST START==="). + +once(pythagorean(3, 4, 5)). +once(pythagorean(3, 3, 5)). + +once(prime(4)). +once(prime(17)). + +{L}/( + findall( + [X, Y], + goldbach(X, Y, 20), + L1 + ), + msort(L1, L) +). diff --git a/HW/Homework5/q2.expected b/HW/Homework5/q2.expected new file mode 100644 index 0000000..990c28d --- /dev/null +++ b/HW/Homework5/q2.expected @@ -0,0 +1,20 @@ + +true. + +false. + +L = [1, 4]. + +false. + +L = [[1, 3, 4]]. + +false. + +L = [[3, 4, 3], [4, 3, 4]]. + +false. + +true. + + diff --git a/HW/Homework5/q2.in b/HW/Homework5/q2.in new file mode 100644 index 0000000..65a3b14 --- /dev/null +++ b/HW/Homework5/q2.in @@ -0,0 +1,38 @@ +use_module(library(yall)). +print("===TEST START==="). + +once(legal_graph(graph([[2, 3], [], [2, 4], [3]]))). +once(legal_graph(graph([]))). + +{L}/( + findall( + X, + edge(graph([[2, 3], [], [2, 4], [3]]), X, 3), + L1 + ), + msort(L1, L) +). +once(edge(graph([[2, 3], [], [2, 4], [3]]), 2, X)). + +{L}/( + findall( + P, + path(graph([[2, 3], [], [2, 4], [3]]), 1, 4, P), + L1 + ), + msort(L1, L) +). +once(path(graph([[2, 3], [], [2, 4], [3]]), 2, 4, _)). + +{L}/( + findall( + P, + circle(graph([[2, 3], [], [2, 4], [3]]), P), + L1 + ), + msort(L1, L) +). + +once(dag(graph([[2, 3], [], [2, 4], [3]]))). +once(dag(graph([[2, 3], [], [2], [3]]))). + diff --git a/HW/Homework5/selfcheck.sh b/HW/Homework5/selfcheck.sh new file mode 100644 index 0000000..ef5e011 --- /dev/null +++ b/HW/Homework5/selfcheck.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +tmpdir="selfcheck_tmp" +test_files=("q1" "q2") +required_files=("hw5_q1.pl" "hw5_q2.pl" "dry.pdf") + +hw5="https://raw.githubusercontent.com/AdiHarif/236319-Spr-2023/master/HW/Homework5/" + +if [ -z "$1" ]; then + echo "Usage: ./"$( basename "$0" )" " + exit +fi + +if [ ! -f "$1" ]; then + echo "Submission zip file not found!" + exit +fi + +rm -rf "$tmpdir" &> /dev/null +if [ -d "$tmpdir" ] + then + echo "Cannot clear tmp directory. Please delete '$tmpdir' manually and try again" + exit +fi +mkdir "$tmpdir" &> /dev/null + +yes | apt update +yes | apt install zip &> /dev/null + +unzip "$1" -d "$tmpdir" &> /dev/null +if [[ $? != 0 ]]; then + echo "Unable to unzip submission file!" + exit +fi + +cd "$tmpdir" +for f in "${required_files[@]}" +do + if [ ! -f $f ]; then + echo "File $f not found!" + exit + fi +done + +if [ $( ls | wc -l ) != ${#required_files[@]} ]; then + echo "There are too many files in the submission" + exit +fi + +cp ../$2/* . &> /dev/null + +for test in "${test_files[@]}" +do + wget "$hw5$test.in" "$hw5$test.expected" &> /dev/null + sleep 3 + if [ ! -f "$test.in" ] || [ ! -f "$test.expected" ]; then + echo "Unable to download test $test!" + exit + fi + swipl -q hw5_$test.pl < $test.in &> $test.rawout + sed '1,/===TEST START===/{N;d}' $test.rawout > $test.out + diff -w $test.out $test.expected + if [[ $? != 0 ]]; then + echo "Failed test $test!" + exit + fi +done + +cd - &> /dev/null +rm -rf "$tmpdir" + +echo "Ok to submit :)" +exit diff --git a/HW/Homework6/q1.expected b/HW/Homework6/q1.expected new file mode 100644 index 0000000..5dcd451 --- /dev/null +++ b/HW/Homework6/q1.expected @@ -0,0 +1,6 @@ + +R = s(s(s(bot))). + +Res = s(s(bot)). + +Res = s(s(s(s(s(s(s(s(s(bot))))))))). diff --git a/HW/Homework6/q1.in b/HW/Homework6/q1.in new file mode 100644 index 0000000..6ce255f --- /dev/null +++ b/HW/Homework6/q1.in @@ -0,0 +1,7 @@ +use_module(library(yall)). +print("===TEST START==="). + +once(add(R,s(s(bot)),s(s(s(s(s(bot))))))). + +once(multiply(s(s(bot)),s(bot),Res)). +once(power(s(s(s(bot))),s(s(bot)),Res)). diff --git a/HW/Homework6/q2.expected b/HW/Homework6/q2.expected new file mode 100644 index 0000000..03bab75 --- /dev/null +++ b/HW/Homework6/q2.expected @@ -0,0 +1,6 @@ + +false. + +L = [[1, 3, 2, 4], [1, 3, 4, 2]]. + +L = [[1], [2], [3, 4]]. diff --git a/HW/Homework6/q2.in b/HW/Homework6/q2.in new file mode 100644 index 0000000..f8fdcc3 --- /dev/null +++ b/HW/Homework6/q2.in @@ -0,0 +1,27 @@ +use_module(library(yall)). +print("===TEST START==="). + +once(topological_sort(graph([[2, 3], [], [2, 4], [3]]), S)). + +{L}/( + findall( + S, + topological_sort(graph([[2, 3], [], [2, 4], []]), S), + L1 + ), + msort(L1, L) +). + + +{L}/( + once(scc(graph([[2, 3], [], [2, 4], [3]]), S)), + findall( + C1, + ( + member(C, S), + msort(C, C1) + ), + L1 + ), + msort(L1, L) +). diff --git a/HW/Homework6/selfcheck.sh b/HW/Homework6/selfcheck.sh new file mode 100644 index 0000000..d008d28 --- /dev/null +++ b/HW/Homework6/selfcheck.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +tmpdir="selfcheck_tmp" +test_files=("q1" "q2") +required_files=("hw6_q1.pl" "hw6_q2.pl" "dry.pdf") + +hw6="https://raw.githubusercontent.com/AdiHarif/236319-Spr-2023/master/HW/Homework6/" + +if [ -z "$1" ]; then + echo "Usage: ./"$( basename "$0" )" [directory with previous solutions]" + exit +fi + +if [ -z "$2" ]; then + echo "Warning - no directory with previous solutions was given (ignore in case you do not use your previous solution)." + echo "Usage: ./"$( basename "$0" )" [directory with previous solutions]" +fi + + +if [ ! -f "$1" ]; then + echo "Submission zip file not found!" + exit +fi + +rm -rf "$tmpdir" &> /dev/null +if [ -d "$tmpdir" ] + then + echo "Cannot clear tmp directory. Please delete '$tmpdir' manually and try again" + exit +fi +mkdir "$tmpdir" &> /dev/null + +yes | apt update &> /dev/null +yes | apt install wget &> /dev/null +yes | apt install zip &> /dev/null + +unzip "$1" -d "$tmpdir" &> /dev/null +if [[ $? != 0 ]]; then + echo "Unable to unzip submission file!" + exit +fi + +cd "$tmpdir" +for f in "${required_files[@]}" +do + if [ ! -f $f ]; then + echo "File $f not found!" + exit + fi +done + +if [ $( ls | wc -l ) != ${#required_files[@]} ]; then + echo "There are too many files in the submission" + exit +fi + +cp ../$2/* . &> /dev/null + +for test in "${test_files[@]}" +do + wget "$hw6$test.in" "$hw6$test.expected" &> /dev/null + sleep 3 + if [ ! -f "$test.in" ] || [ ! -f "$test.expected" ]; then + echo "Unable to download test $test!" + exit + fi + swipl -q hw6_$test.pl < $test.in &> $test.rawout + sed '1,/===TEST START===/{N;d}' $test.rawout > $test.out + diff -wB $test.out $test.expected + if [[ $? != 0 ]]; then + echo "Failed test $test!" + exit + fi +done + +cd - &> /dev/null +rm -rf "$tmpdir" + +echo "Ok to submit :)" +exit diff --git a/Material/Tutorials/imgs/docker.png b/Material/Tutorials/imgs/docker.png new file mode 100644 index 0000000000000000000000000000000000000000..580f2a1da0c12ad0b492215a510e0534cb4339e8 GIT binary patch literal 47520 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

hx_U_2he?{J&tjKhvWQi8j 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 Z=uWooiJtnDoxdX6$k6P1ng0E#{{wPof5HF& literal 0 HcmV?d00001 diff --git a/Material/Tutorials/misc/docker.md b/Material/Tutorials/misc/docker.md new file mode 100644 index 0000000..4dc8a9a --- /dev/null +++ b/Material/Tutorials/misc/docker.md @@ -0,0 +1,149 @@ + +# Docker +![docker](../../imgs/docker.png) + +--- + +## important notes + +Docker is not included in this course materials, i.e. you wont be asked about it in homework or final exam. + +However, you are expected to use it in order to check you homework before submitting. + +--- + +## what is docker? + +Docker is a platform for running *containerized* applications. + +It provides a standard way to package an application and its dependencies into a container which can be easily run on any compatible host. + + + +### use cases + +Docker is widely used for testing, devops automation, deploying scalable web servers and many more uses. + +In general it is used whenever a custom running environment is required. + +--- + +## how does docker works + +When we work with docker, there are three main components we consider: `Dockerfile`, `Docker Image` and `Docker Container`. + + + +### Dockerfile + +This is a text file which is the "recipe" of how to create the virtual environment. + +For example it may look like this: +```docker +# syntax=docker/dockerfile:1 +FROM ubuntu:18.04 +COPY . /app +RUN make /app +CMD python /app/app.py +``` + + + +### docker image + +This is the basis of our environments. The Docker Image is created once out of the docker file and then can be used to create as many containers as needed. + + + +### docker container + +This is our virtual environment. It contains everything included in the image, and is isolated from our host environment. + + + +--- + +## why do we use docker in this course + +Throughout the semester you will be required to use various tools in your homework assignments such as FreePascal compiler, ML interpreter and so on... + + + + +### why do we use docker in this course + +These dependencies are not installed on our department's server. +Therefor we wanted to supply you with an environment where you can find and use all the tools you need for this course. + + + +### why do we use docker in this course + +In the docker image we supply, you can find any tool needed to run you homework assignments. + +Furthermore, these are the same images your assignments will be tested with. + +--- + + + +## how to use docker + +First of all, you need to install docker. for installation please refer to [this guide](https://docs.google.com/document/d/1JsjRnOC4oHi4SPF6R9xOravEu7tHIAw_mMqFgjRo_4E/edit) (can be found in our website) + + + +### how to use docker + +We already created docker images for you to use throughout this course. you will only need to run containers using these images and run your tests. + + + +### running docker + +In every homework assignment we tell you which docker image to use for that assignment. + +For example, in order to test the first assignment you were supplied with an image called **twyair/safot-hw:1** + + + + +### running docker + +When you try to run a container based on a certain image for the first time, it will be download the image from the docker registry. + +That way, you don't need to manage your images and docker will do it for you! + + + + +### running docker + +In order to run a new container, you can use the following command: + +> `docker run --rm -it -v` +**\:\ \** + +for example: + +```shell +docker run -it --rm -v $(pwd):/source twyair/safot-hw:1 +``` + + + +### Running docker +> `docker run --rm -it -v` +**\:\ \** + +* `--rm` deletes the container after it exits. +* `-it` opens the container in *interactive mode*. this will allow you to use the container's shell and run your commands inside. + + + +### Running docker +> `docker run --rm -it -v` +**\:\ \** + +* `-v` will mount a folder from the host file system so you can use it inside the container. In the example showed earlier the current working directory will be mounted to "source" directory inside the container. +* `image_name` is the image the new container will be created from. diff --git a/Material/Tutorials/pascal/overview.md b/Material/Tutorials/pascal/overview.md index 62fc875..1a819e5 100644 --- a/Material/Tutorials/pascal/overview.md +++ b/Material/Tutorials/pascal/overview.md @@ -20,7 +20,7 @@ program HelloWorld; { Definitions are placed here - types, variables, procedures, functions, ...} begin - WriteLn('Hello, World!'); + WriteLn('Hello, World!') { More statements can be added here } end. ``` @@ -53,7 +53,7 @@ program HelloWorld; var str : string; begin str := 'Hello, World!'; - WriteLn(str); + WriteLn(str) end. ``` @@ -348,7 +348,7 @@ type Point = record letter: char; case UsePolar : boolean of False : (X, Y, Z : Real); - True : (R, theta, phi : Real); + True : (R, theta, phi : Real) end; ``` @@ -361,14 +361,14 @@ The `Point` record will have different fields based on the value of `UsePolar`. if (not p.UsePolar) then r := CubicRoot(p.X*p.X + p.Y*p.Y + p.Z*p.Z) else - r := p.R; + r := p.R end. ``` --- -## arrays in pascal +## Arrays in Pascal > `array` **index-type** `of` **element-type** @@ -432,13 +432,13 @@ s[i] := '?'; { s = 'Hello, World?' } ## functions -pascal functions always return a value +Pascal functions always return a value ```pascal function myFunc(a: integer; b: real): real; begin myFunc := a * b // that's how you set the return value -end +end; ``` * in this example `a` and `b` are passed by-value @@ -454,7 +454,7 @@ procedure myProc(var a: boolean); begin WriteLn('Hello, World!'); a := true -end +end; ``` `var` here means "pass by reference" diff --git a/Material/Tutorials/prolog/clp.md b/Material/Tutorials/prolog/clp.md index 0d4251f..349450f 100644 --- a/Material/Tutorials/prolog/clp.md +++ b/Material/Tutorials/prolog/clp.md @@ -6,20 +6,21 @@ ### intro -* `CLP(FD)` is a library -* CLP = **C**onstraint **L**ogic **P**rogramming -* FD = **F**inite **D**omains, namely integers -* use it to solve a set of constraints over integers +`CLP(FD)` is a library that help us use constraints over integer values. +* CLP = **C**onstraint **L**ogic **P**rogramming. +* FD = **F**inite **D**omains. -write in the interpreter: +### usage + +in order to use `CLP(FD)`, write the following in the REPL: ```prolog use_module(library(clpfd)). ``` -or write the following at the top of a file: +...or write the following at the top of a file: ```prolog :- use_module(library(clpfd)). @@ -28,13 +29,9 @@ or write the following at the top of a file: --- -### arithmetic constraints - - - -#### comparison operators +### comparison operators -the comparison operators are almost the same but prefixed by `#` +The comparison operators are almost the same but prefixed by `#` ```prolog X #> Y. @@ -47,10 +44,12 @@ X #\= Y. +### comparison operators + `X` and `Y` can be any arithmetic expression: -* an integer value -* a variable +* An integer value. +* A variable. * `-Expr` * `Expr1 @ Expr2` where `@` is replaced by `+` `*` `-` `^` `//` `div` `mod` `rem` * `abs(Expr)` @@ -59,42 +58,52 @@ X #\= Y. -how are these different from the regular comparison operators? +### comparison operators + +How are these different from the regular comparison operators? ```prolog ?- X + 2 =:= Y + X. ?- X + 2 #= Y + X. +% Y = 2, X in inf..sup. ``` -the `#`-operators don't require that any of the variables are instantiated +The `#`-operators don't require that any of the variables are instantiated --- ### domains -`CLP(FD)` can give a domain as a solution +`CLP(FD)` can give a domain as a solution: ```prolog ?- 0 #< X, X #< 5. +% X in 1..4. ``` -in a domain `sup` is for supremum and `inf` is for infimum +### domains + +In a domain, `sup` is for supremum and `inf` is for infimum. ```prolog ?- 0 #< X. +% X in 1..sup. ?- X #< 5. +% X in inf..4. ``` -we can use the `in` operator in our code +### domains + +We can use the `in` operator in our code. ```prolog ?- X in 1..5. @@ -103,20 +112,24 @@ we can use the `in` operator in our code +### domains + `\/` is used for domains union ```prolog ?- X in 1..5, X #\= 2. +% X in 1\/3..5. ?- X in 1\/2\/3. +% X in 1..3. ``` -#### labeling +### labeling -`indomain(X)` is used to successively bind `X` to all integers of its domain +`indomain(X)` is used to successively bind `X` to all integers of its domain: ```prolog ?- X in 1..3, indomain(X). @@ -125,7 +138,9 @@ we can use the `in` operator in our code -`indomain` must always terminate +### labeling + +`indomain` must always terminate: ```prolog ?- X in 0..sup, indomain(X). @@ -134,7 +149,9 @@ we can use the `in` operator in our code -`label` is just like `indomain` but for a list of variables +### labeling + +`label` is just like `indomain` but for a list of variables: ```prolog ?- 0 #=< N, N #< 17, 0 #< A, 0 #< B, N * N #= A * A + B * B, label([N, A, B]). @@ -145,7 +162,7 @@ we can use the `in` operator in our code ### question -implement the predicate `change/2`. `change(S, L)` is true iff: +Implement the predicate `change/2`. `change(S, L)` is true iff: * `S` is a positive integer * `L` is a **sorted** (descending) list made of the integers `1`, `5`, `10` @@ -160,10 +177,15 @@ implement the predicate `change/2`. `change(S, L)` is true iff: -you can use the predicate `repeat/3`. `repeat(N, C, L)` is true iff: +> Hint - you can use a helper predicate `repeat/3`. `repeat(N, C, L)` is true iff: +> * `N` is a conrete non-negative integer. +> * `L` is a list of `N` `C`s. + + -* `N` is a conrete non-negative integer -* `L` is a list of `N` `C`s +> Another Hint - you can use a helper predicate `build/2`. `build(Ls, L)` is true iff: +> * `Ls` is a list of pairs `[N, C]` where `N` is a concrete non-negative integer. +> * `L` is a list containing `Ni` `Ci`s, for each `i` in range. diff --git a/Material/Tutorials/prolog/control-predicates.md b/Material/Tutorials/prolog/control-predicates.md index 0a76705..d76a682 100644 --- a/Material/Tutorials/prolog/control-predicates.md +++ b/Material/Tutorials/prolog/control-predicates.md @@ -254,11 +254,11 @@ define the predicate `maybe_once/1` that accepts a predicate and succeeds at mos for example, for the dataset: ```prolog -ta(yair). +ta(adi). ta(andrey). ``` -and for the goal `maybe_once(ta(X))`, we would expect to get `X = yair.`. +and for the goal `maybe_once(ta(X))`, we would expect to get `X = adi`. @@ -318,7 +318,7 @@ this is because `->/2` does not discard the second clause. it is possible to define an "else" branch, for example: ```prolog -grade(X, Y) :- X #>= 100 -> Y = 100 ; Y = X. +grade(X, Y) :- X >= 100 -> Y = 100 ; Y = X. ``` diff --git a/Material/Tutorials/prolog/exam-questions.md b/Material/Tutorials/prolog/exam-questions.md index f60377f..4fc6ac6 100644 --- a/Material/Tutorials/prolog/exam-questions.md +++ b/Material/Tutorials/prolog/exam-questions.md @@ -232,59 +232,6 @@ cycle(X) :- edge(Y, X), path(X, Y, _, _). ### question 5 -implement `is_prime(X)`, wher `X` is a concrete integer - -```prolog -``` - - - - -```prolog -is_prime(2). -is_prime(P) :- - P > 2, \+ divisible(P,2). - -divisible(N,L) :- N mod L =:= 0. -divisible(N,L) :- - L * L < N, L2 is L + 1, divisible(N,L2). -``` - - - - -Goldbach's conjecture says that every positive even number greater than 2 is the sum of two prime numbers. - - - -implement `goldbach/3`. - -* `goldbach(A,B,S)` is true if `A+B=S`, `A` and `B` are prime numbers and `S` is an even number. - -* either `S` is concrete or `A` and `B` are concrete - - - -```prolog -``` - - - - -```prolog -:- use_module(library(clpfd)). -goldbach(A,B,S) :- - S mod 2 #= 0, - A #>= 2, A #< S, A + B #= S , A #=< B, - label([A, B, S]), - is_prime(A), is_prime(B). -``` - - ---- - -### question 6 - how can a knight jump on an NxN chessboard in such a way that it visits every square exactly once? diff --git a/Material/Tutorials/prolog/introduction.md b/Material/Tutorials/prolog/introduction.md index 7dad29b..1e31312 100644 --- a/Material/Tutorials/prolog/introduction.md +++ b/Material/Tutorials/prolog/introduction.md @@ -4,15 +4,66 @@ --- -### basic constructs +### What is Prolog? + +* Prolog - **Pro**gramming in **Log**ic. +* A compiled, *Untyped*, *declarative* language. +* Originally developed for AI applications. +* First released in the 1970s. -the basic constructs of prolog are terms and statements +### Logic/Declarative Programming + +Common use cases include: +* Database queries. +* Rule-based systems. +* Automated Reasoning. + +In general - any problem that can be easily expressed in terms of logic or a set of constraints. + + + +### Advantages + +* Concise and readable code. +* Easy to express complex problems. +* Modular and Extensible. + + + +### Disadvantages + +* Not suitable for all problems. +* Usually not as performant as other languages. + +--- + +### Using Prolog + +A Prolog program consists of a set of *facts* and *rules*. Given a program, we can make *queries* about these rules using a REPL. + +In this class we will use *Swi-Prolog*. + + + +### Typical Workflows + +Usually we will use Prolog in one of two ways: +1. Open the REPL over a program and make queries. +1. Dynamically add facts and rules (or even load programs) using the REPL. + +--- + +### Terms + +Terms are the basic building blocks of Prolog programs. + +Analogous to *expressions* in other languages. -#### terms - atoms +### terms - atoms the simplest term is an **atom**, the following are atoms: @@ -25,24 +76,24 @@ $<@ -an **atom** is +### String atoms -* a string of letters, digits, and an underscore starting with a **lower-case letter**: `anna` `x_25` `nil` -* a string of special characters (`+ - * / < > = : . & _ ~`): `$<@` `<---->` `.:.` -* a string of characters enclosed in single quotes: `'Tom'` `'2A$'` +* A string of letters, digits, and an underscore starting with a **lower-case letter**: `anna` `x_25` `nil` +* A string of special characters (`+ - * / < > = : . & _ ~`): `$<@` `<---->` `.:.` +* A string of characters enclosed in single quotes: `'Tom'` `'2A$'` -#### terms - numbers +### Numeric atoms -* integers: `123` `-42` -* real numbers: `3.14` `-0.573` `2.4e3` +* Integers: `123` `-42` +* Real numbers: `3.14` `-0.573` `2.4e3` -#### terms - variables +#### Variables -a **variable** is a string of letters, digits and an underscore starting with an upper-case letter or an underscore +A **variable** is a string of letters, digits and an underscore starting with an upper-case letter or an underscore ```prolog X_25 @@ -51,42 +102,32 @@ _result -#### compound terms +### compound terms -a **compound term** comprises a functor and arguments +A **compound term** comprises a *functor* and *arguments*. ```prolog course(236319, pl) ``` -a functor `f` of arity `n` is denoted `f/n` +A functor `f` of arity `n` is denoted `f/n`. -#### terminology - -* a term is **ground** (קונקרטי) if it contains no variables -* a **goal** is an atom or a compound term -* a **predicate** (פרדיקט) is a functor for which a list of clauses is defined -* a **clause** (פסוקית) is a fact or a rule - - - -#### facts - -a **fact** (עובדה) is a kind of statement +A **fact** is a term that we define to be true in our program. ```prolog eats(bunny, carrot). ``` -this fact states that the **predicate** `eats` holds for the atoms `bear` and `honey` +This fact states that the **predicate** `eats` holds for the atoms `bunny` and `carrot` terms. -facts can have any arity + +Facts can have any arity: ```prolog summer. @@ -97,7 +138,7 @@ plus(2, 3, 5). -a finite set of facts constitutes a program +A finite set of facts constitutes a program: ```prolog mammal(rat). @@ -112,29 +153,29 @@ eats(salmon, warm). -facts can contain variables +Facts can contain variables: ```prolog likes(X, course236319). ``` -variables are universally quantified +Variables are universally quantified, so this fact is equivalent to: -$$\forall X,likes(X, course236319)$$ +$$\forall X: likes(X, course236319)$$ -#### queries +### queries -a **query** (שאילתה) is a conjunction of goals +A **query** is a conjunction of goals: ```prolog ?- eats(X, salmon), eats(X, honey). ``` -variables are existentially quantified +Variables are existentially quantified, so this query is equivalent to: $$\exists X,eats(X, salmon) \land eats(X, honey)$$ @@ -142,7 +183,7 @@ $$\exists X,eats(X, salmon) \land eats(X, honey)$$ #### rules -a **rule** (חוק, כלל) is a statement which enables us to define new relationships in terms of existing ones +A **rule** is a statement which enables us to define new relationships in terms of existing ones: ```prolog predicate(term1, ..., termN) :- goal1, ..., goalN. @@ -169,19 +210,26 @@ survival_dependency(X, Y) :- --- -### writing Prolog programs +### dynamically loading programs -* create a file named `prog.pl` -* write clauses in `prog.pl` -* enter the prolog interpreter -* type `consult(prog.pl)` -* query the interpreter +Given a program in a file `prog.pl` we can load it into the REPL using `consult/1`: - +```prolog +?- consult('prog.pl'). +/* or */ +?- consult(prog). +``` + +Also possible using brackets: + +```prolog +?- [prog]. +``` -#### dynamic clauses + +### dynamic rules -you can add clauses dynamically +You can add clauses dynamically: ```prolog :- assertz(eats(bear, tuna)). @@ -198,11 +246,17 @@ you can add clauses dynamically ``` -`asserta` asserts the clause as first clause of the predicate while `assertz` asserts it as last clause +`asserta` asserts the clause as first clause of the predicate while `assertz` asserts it as last clause. + + + +### dynamic rules + +Note that facts and rules from a compiled or loaded program are not dynamic by default, so you cannot add to them using assertions. -dynamically remove a clause using `retract/1` +Dynamically remove a clause using `retract/1`: ```prolog :- assertz(q(a)). @@ -223,7 +277,7 @@ dynamically remove a clause using `retract/1` -dynamically remove clauses using `retractall/1` +Dynamically remove all clauses using `retractall/1`: ```prolog :- assertz(q_2(a)). @@ -245,7 +299,7 @@ dynamically remove clauses using `retractall/1` -dynamically remove a predicate using `abolish/1` +Dynamically remove a predicate using `abolish/1`: ```prolog :- assertz(p(a)). @@ -265,27 +319,16 @@ dynamically remove a predicate using `abolish/1` --- -### meaning of a prolog program - -* declarative meaning - * the inference algorithm is an implementation detail - * not always easy to achieve -* procedural meaning - * but thinking procedurally makes it harder to come up with an elegant solution - * beats the purpose of the paradigm - ---- - ### matching -two terms match if: +Two terms match if: -* they are identical -* the variables in both terms can be instantiated to make the terms identical +* They are identical. +* The variables in both terms can be instantiated to make the terms identical. -the operator `=` performs matching +The `=` operator performs matching ```prolog ?- course(N, S, 95) = course(X, fall, G). @@ -303,23 +346,23 @@ the operator `=` performs matching -#### matching rules +### matching rules -terms `S` and `T` match if: +Terms `S` and `T` match if: -* `S` and `T` are the same atom -* `S` and `T` are the same number -* if one is a variable it's instantiated to the other -* if `S` and `T` are compound terms, they match iff: - * they have the same functor and arity - * all their corresponding arguments match - * the variable instantiations are compatible +* `S` and `T` are the same atom. +* `S` and `T` are the same number. +* If one is a variable which is instantiated to the other. +* If `S` and `T` are compound terms, they match iff: + * They have the same functor and arity. + * All their corresponding arguments match. + * The variable instantiations are compatible. -#### geometric example +### geometric example -use compound terms to represent geometric shapes +Use compound terms to represent geometric shapes. ```prolog point(1, 1) @@ -329,6 +372,8 @@ triangle( point(4, 2), point(6, 4), point(7, 1) ) +### geometric example + ```prolog ?- triangle(point(1, 1), A, point(2, 3)) = @@ -338,9 +383,9 @@ triangle(X, point(4, Y), point(2, Z)). -#### matching as means of computation +### matching as means of computation -facts: +Facts: ```prolog vertical(seg( @@ -350,7 +395,7 @@ vertical(seg( ``` -queries: +Queries: ```prolog ?- vertical(seg(point(1, 1), point(1, 2))). @@ -363,10 +408,10 @@ queries: --- -### arithmetic +### arithmetic operations -* the operators `+ - * / div mod` are (infix) binary relations -* but they are arithmetic operators after the operator `is` +* The operators `+ - * / div mod` are (infix) binary relations. +* But they are considered arithmetic operators after the operator `is`. ```prolog ?- X = 1 + 2. @@ -377,7 +422,7 @@ queries: -#### comparison operators +### comparison operators ```prolog X > Y @@ -390,7 +435,7 @@ X =\= Y % not equal -the comparison operators also force evaluation +The comparison operators also force evaluation: ```prolog ?- 11 * 6 = 66. @@ -403,8 +448,8 @@ the comparison operators also force evaluation #### `=` VS. `=:=` -* `=` is used for matching and may instantiate variables -* `=:=` causes an arithmetic evaluation of its operands and cannot instantiate variables +* `=` is used for matching and may instantiate variables. +* `=:=` causes an arithmetic evaluation of its operands and cannot instantiate variables. ```prolog ?- 1 + X = Y + 2. @@ -415,7 +460,7 @@ the comparison operators also force evaluation -#### GCD +#### Example - GCD ```prolog gcd(X, X, X). @@ -442,15 +487,15 @@ gcd(X, Y, D) :- #### conjunction `,/2` -the goal `(G1, G2)` succeeds if `G1` and `G2` succeed +The goal `(G1, G2)` succeeds if `G1` and `G2` succeed. #### disjunction `;/2` -the goal `(G1 ; G2)` succeeds if `G1` or `G2` succeed +The goal `(G1 ; G2)` succeeds if `G1` or `G2` succeed. -defined as follows: +Defined as follows: ```prolog (G1 ; G2) :- G1. @@ -461,21 +506,21 @@ defined as follows: #### true -the predicate `true/0` always succeeds +The predicate `true/0` always succeeds. #### false -the predicates `false/0` and `fail/0` always fail +The predicates `false/0` and `fail/0` always fail. #### negation as failure -* the negation predicate is `\+/1` -* for known predicates, prolog works under a closed world assumption - if something can't be proved then it is false -* it is not logical negation! +* The negation predicate is `\+/1`. +* It is not logical negation! +* For known predicates, prolog works under a closed world assumption - if something can't be proved then it is false. @@ -493,7 +538,7 @@ person(cindy). -it might not work like you'd expect +It might not work like you'd expect ```prolog ?- person(X). @@ -505,7 +550,7 @@ it might not work like you'd expect ``` -why doesn't prolog answer with `X = rick` or simply with `true`? +Why doesn't prolog answer with `X = rick` or simply with `true`? @@ -516,7 +561,7 @@ why doesn't prolog answer with `X = rick` or simply with `true`? -`\+/1` allows for non-monotonic reasoning - a fact can become false by adding clauses to the database +`\+/1` allows for non-monotonic reasoning - a fact can become false by adding clauses to the database: ```prolog illegal(murder). @@ -538,7 +583,7 @@ illegal(theft). ### exercise - family tree -you have a database with the following predicate +Given a database with the following predicate: ```prolog parent(X, Y). % X is Y's parent @@ -552,7 +597,7 @@ parent(cain, enoch). ``` -define a predicate `grandparent(X)` that holds when `X` is a grandparent +Define a predicate `grandparent(X)` that holds when `X` is a grandparent. @@ -574,9 +619,9 @@ grandparent(X) :- parent(X, Y), parent(Y, _). -define a predicate `nuclear(X, Y)` that holds when `X` and `Y` are in the same nuclear family +Define a predicate `nuclear(X, Y)` that holds when `X` and `Y` are in the same nuclear family. -a nuclear family consists of 2 parents and their common children +A nuclear family (in our example) consists of 2 parents and their common children. @@ -609,12 +654,12 @@ nuclear(X, Y) :- ### exercise - binary trees -we represent binary trees as terms: +We represent binary trees as terms: -* `nil` is the empty tree -* `node(N, Tl, Tr)` is a tree node where `N` is some number and `Tl` and `Tr` are binary trees +* `nil` is the empty tree. +* `node(N, Tl, Tr)` is a tree node where `N` is some number and `Tl` and `Tr` are binary trees. -define a predicate `tree_size(T, S)` such that `T` is a binary tree and `S` is its size +Define a predicate `tree_size(T, S)` such that `T` is a binary tree and `S` is its size @@ -649,9 +694,9 @@ tree_size(node(_, Tl, Tr), S) :- -define a predicate `tree_max(T, M)` such that `T` is a binary tree and `M` is the max of the values of `T`'s nodes +Define a predicate `tree_max(T, M)` such that `T` is a binary tree and `M` is the max of the values of `T`'s nodes. -you may use the arithmetic function `max/2` +You may use the arithmetic function `max/2` ```prolog ... @@ -690,11 +735,9 @@ tree_max(node(N, Tl, Tr), M) :- -define a predicate `perfect_tree(T, H)` such that `T` is a perfect binary tree and `H` is its height - -a node's value should be its height +A perfect binary tree is a binary tree in which all interior nodes have two children and all leaves have the same depth. Also, the value of each interior node is equal to its depth. -a perfect binary tree is a binary tree in which all interior nodes have two children and all leaves have the same depth +Define a predicate `perfect_tree(T, H)` such that `T` is a perfect binary tree and `H` is its height. diff --git a/Material/Tutorials/prolog/lists.md b/Material/Tutorials/prolog/lists.md index 8b31a74..fd3a4ca 100644 --- a/Material/Tutorials/prolog/lists.md +++ b/Material/Tutorials/prolog/lists.md @@ -4,7 +4,9 @@ --- -a **list** is a sequence of any number of terms +### Lists + +In Prolog, a **list** is a sequence of any number of terms, separated by commas and enclosed in square brackets: ```prolog [1, 2, 3] @@ -14,23 +16,23 @@ a **list** is a sequence of any number of terms -a list is actually a pair of a head and a tail +In fact, a list is actually a pair of a head and a tail (which is a list itself): ```prolog [Head | Tail] [A, B, C] = [A | [B | [C | []]]] = [A, B | [C]] ``` +> Kind of like SML lists. + --- +## Built-in list predicates + + ### length/2 -```prolog -length([], 0). -length([_|Tail], N) :- - length(Tail, N1), - N is 1 + N1. -``` +`length(L, N)` is satisfied when `L` is a list of length `N`. ```prolog ?- length([a, b, [c, d], e], N). @@ -41,36 +43,41 @@ length([_|Tail], N) :- -### length with CLP(FD) +### length/2 + +`length/2` is implemented as follows: ```prolog length([], 0). length([_|Tail], N) :- - N #= N1 + 1, - length(Tail, N1). + length(Tail, N1), + N is 1 + N1. ``` ---- - -### list predicates - -#### is_list/1 (predefined) +### is_list/1 + +`is_list(X)` is satisfied when `X` is a list. ```prolog -... +?- is_list(17). +?- is_list([1, 2, 3]). ``` +How would you implement this predicate? + ```prolog -?- is_list(17). -?- is_list([1, 2, 3]). +... ``` + +### is_list/1 + ```prolog is_list([]). is_list([X|Xs]) :- is_list(Xs). @@ -79,20 +86,26 @@ is_list([X|Xs]) :- is_list(Xs). -#### member/2 (predefined) +### member/2 + +`member(X, L)` is satisfied when `X` is a member of `L`. ```prolog -... +?- member(X, [17, 13, 2, 5]). ``` +How would you implement this predicate? + ```prolog -?- member(X, [17, 13, 2, 5]). +... ``` +### member/2 + ```prolog member(X, [X|Xs]). member(X, [Y|Ys]) :- member(X, Ys). @@ -101,20 +114,26 @@ member(X, [Y|Ys]) :- member(X, Ys). -#### prefix/2 +### prefix/2 + +`prefix(X, L)` is satisfied when `X` is a prefix of `L`. ```prolog -... +?- prefix(X, [a, b, c, d]). ``` +How would you implement this predicate? + ```prolog -?- prefix(X, [a, b, c, d]). +... ``` +### prefix/2 + ```prolog prefix([], L). prefix([X|Xs], [X|Ys]) :- prefix(Xs, Ys). @@ -125,18 +144,24 @@ prefix([X|Xs], [X|Ys]) :- prefix(Xs, Ys). #### suffix/2 +`suffix(X, L)` is satisfied when `X` is a suffix of `L`. + ```prolog -... +?- suffix(X, [1, 2, 3]). ``` +How would you implement this predicate? + ```prolog -?- suffix(X, [1, 2, 3]). +... ``` +#### suffix/2 + ```prolog suffix(Xs, Xs). suffix(Xs, [Y|Ys]) :- suffix(Xs, Ys). @@ -145,13 +170,53 @@ suffix(Xs, [Y|Ys]) :- suffix(Xs, Ys). -#### del/3 +#### nth0/3 and nth1/3 + +`nth0(I, L, E)` is satisfied when `E` is the `I`'th element of `L` starting with index 0. ```prolog -del(X, L, R) +?- nth0(1, [1, 2, 3], X). ``` + + +> `nth1/3` is the same as `nth0/3` but starts with index 1. + + + +#### max_list/2 and min_list/2 + +`max_list(L, M)` is satisfied when `M` is the maximum element of `L`. -`R` is `L` without one of the occurrences of `X` + +```prolog +?- max_list([1, 2, 3], X). +``` + + +> `min_list/2` is the same as `max_list/2` but for the minimum element. + + + +#### flatten/2 + +`flatten(L, F)` is satisfied when `F` is the (recursively) flattened version of `L`. + +```prolog +?- flatten([1, [2, [3, 4], 5], 6], X). +``` + + +--- + +## List Exercises + +Implement the following (non built-in) predicates. + + + +#### del/3 + +`del(X, L, R)` is satisfied when `R` is `L` without one of the occurrences of `X`. ```prolog ... @@ -165,16 +230,25 @@ del(X, L, R) +#### del/3 + ```prolog del(X, [X|Xs], Xs). del(X, [Y|Ys], [Y|Zs]) :- del(X, Ys, Zs). ``` +```prolog +?- del(2, [1, 2, 3, 2, 3, 2], X). +``` + + #### insert/3 +`Insert(X, L, R)` is satisfied when `R` is `L` with an additional occurrence of `X`. + ```prolog ... ``` @@ -187,15 +261,26 @@ del(X, [Y|Ys], [Y|Zs]) :- del(X, Ys, Zs). +#### insert/3 + ```prolog insert(X, L, R) :- del(X, R, L). ``` +```prolog +?- insert(3, [1, 2, 3], X). +``` + + + + #### append/3 +`append(X, Y, Z)` is satisfied when `Z` is the concatenation of `X` and `Y` (in that order). + ```prolog ... ``` @@ -208,12 +293,19 @@ insert(X, L, R) :- del(X, R, L). +#### append/3 + ```prolog append([], Ys, Ys). append([X|Xs], Ys, [X|Zs]) :- append(Xs, Ys, Zs). ``` +```prolog +?- append([1, 2], [3, 4, 5], X). +``` + + #### define member/2 using append/3 @@ -234,6 +326,8 @@ member(X, Xs) :- append(_, [X|_], Xs). #### sublist/2 +`sublist(X, Y)` is satisfied when `X` is a sublist of `Y`. + ```prolog ... ``` @@ -241,6 +335,8 @@ member(X, Xs) :- append(_, [X|_], Xs). +#### sublist/2 + ```prolog sublist(Xs, Ys) :- append(As, Bs, Ys), @@ -265,6 +361,8 @@ sublist(Xs, Ys) :- #### permutation/2 +`permutation(X, Y)` is satisfied when `X` is a permutation of `Y`. + ```prolog ... ``` @@ -277,6 +375,8 @@ sublist(Xs, Ys) :- +#### permutation/2 + ```prolog permutation([], []). permutation([X|L], P) :- @@ -284,3 +384,8 @@ permutation([X|L], P) :- insert(X, L1, P). ``` + +```prolog +?- permutation([1, 2, 3], X). +``` + diff --git a/Material/Tutorials/sml/datatypes.md b/Material/Tutorials/sml/datatypes.md index 823f5d1..dd3841a 100644 --- a/Material/Tutorials/sml/datatypes.md +++ b/Material/Tutorials/sml/datatypes.md @@ -8,21 +8,20 @@ `datatype` is a mechanism for defining **new** types -* is **created** using the `datatype` keyword -* **realizes** the theoretical type constructor of disjoint union -* **generalizes** - * the `null` pointer of C, Pascal, Java - * enumerated types, as found in Pascal, C, and Java - * union types, as found in Pascal and C - * branding, as done by Pascal's TYPE, but difficult to achieve in Java and C -* is **essential**: cannot do lists, trees, etc., without it +* **Implements** the theoretical type constructor of disjoint union +* **Generalizes**: + * The `NULL` pointer of C, Pascal, Java + * Enumerated types, as found in Pascal, C, and Java + * Union types, as found in Pascal and C + * Branding, as done by Pascal's TYPE, but difficult to achieve in Java and C +* Is **essential**: cannot do lists, trees, etc., without it -NOTE: - -don't confuse with with `type` which is a mechanism for giving new names to existing types +> Not to be confused with `type`, which is a mechanism for giving new names to existing types +### datatype + `datatype` is an SML type constructor: * given: a list of types and list of tags @@ -32,11 +31,9 @@ don't confuse with with `type` which is a mechanism for giving new names to exis ### Concrete Datatypes -`datatype` creates new types +Types created using `datatype` are **concrete** - these can be constructed and taken apart -* these datatypes are **concrete** (not abstract) -* concrete datatypes can be inspected - constructed and taken apart -* ML's datatypes have two kinds of values: **atomic** and **composite** +ML's datatypes have two kinds of values: **atomic** and **composite** --- @@ -53,6 +50,8 @@ only; +### Enumeration Types + order doesn't matter ```sml @@ -62,7 +61,9 @@ datatype bool' = true' | false'; -allows pattern matching +### Enumeration Types + +Datatypes allow pattern matching: ```sml datatype piece = king | queen | rook | bishop | knight | pawn; @@ -81,33 +82,37 @@ value bishop; ### Type Branding -Newton's second law +If we use builtin types only, we may insert unwanted errors easily: ```sml fun a m f = f/m; -val (body, engine) = (0.0122, 50.0); +val (body_mass, engine_force) = (0.0122, 50.0); -a engine body; (* oops *) +a engine_force body_mass; (* oops *) ``` -type aliasing doesn't help +### Type Branding + +Type aliasing doesn't help ```sml type mass = real and force = real and acceleration = real; fun a (m:mass) (f:force) : acceleration = f/m; -a engine body; (* still oops *) +a engine_force body_mass; (* still oops *) ``` -simulate branding using `datatype` +### Type Branding + +Simulate branding using `datatype`: ```sml datatype mass = Kg of real; @@ -129,14 +134,7 @@ a engine body; (*Error*) ### Constructors (of SML) -the term *constructor* may be confusing - -* constructors of classes, as in C++ -* constructors of types, as in abstract type constructors - - - -in SML constructors are functions +In SML constructors are functions: ```sml datatype mass = Kg of real; @@ -181,17 +179,15 @@ begin end ``` -type safety? none. +Type safety? none. -* same memory block is allocated to either (nothing) / (length) / (radius) / (width + height) -* programmer's responsibility to make sure that only the correct fields are accessed +* Same memory block is allocated to either (nothing) / (length) / (radius) / (width + height) +* Its the rogrammer's responsibility to make sure that only the correct fields are accessed --- ## redoing variant records with datatype -in SML - code is shorter - ```sml datatype shape = point @@ -210,7 +206,9 @@ NOTE: -in SML - type safety +### Pattern Matching + +In SML we also get type safety and pattern matching out of the box: ```sml fun area (point | Line _) = 0.0 @@ -225,26 +223,7 @@ pattern matching is a generalization of switch --- -### Pattern Matching - -```sml -val line = Line 5.3; -``` - - -```sml -val Line length = line; -``` - - -```sml -val Circle radius = line; -``` - - - - -⚠️ constructors cannot be rebound by `val` +⚠️ Constructors cannot be rebound by `val` ```sml val point = point; (*OK*) @@ -256,7 +235,7 @@ val point = 5.3; (*Error*) ### Recursive Datatypes -🛈 every `intlist` is either `NIL` or `head $$ tail` +🛈 Every `intlist` is either `NIL` or `head $$ tail` ```sml datatype intlist = @@ -274,7 +253,7 @@ fun length NIL = 0 ### Polymorphic Datatypes -🛈 every `list` is either `nil` or `head::tail` +🛈 Every `list` is either `nil` or `head::tail` ```sml datatype 'a list = @@ -288,7 +267,9 @@ infixr ::; ``` - +--- + +### Option Example ```sml datatype 'a option = NONE | SOME of 'a; @@ -304,6 +285,8 @@ head (tl [1]); +### Union Example + ```sml datatype ('a, 'b) union = type1 of 'a | type2 of 'b; @@ -346,14 +329,14 @@ size tree4; --- ### Binary Search Trees - -* implement an associative array using trees -* the keys are `int`s -* values may be anything -* assumption: the tree is sorted +Example: implementing an associative map using trees +* Keys are `int`s +* Values may be anything +* Assumption: the tree is sorted +### Binary Search Trees ```sml val cmp = Int.compare; @@ -367,8 +350,9 @@ fun get (Br ((node_k, v), left, right)) k = +### Binary Search Trees Excercise -implement `insert` that takes a BST, a key `k` and a value `v` and returns an updated BST with the node `(k, v)` +Implement `insert` that takes a BST, a key `k` and a value `v` and returns an updated BST with the node `(k, v)` ```sml val insert = fn : (int * 'a) tree -> int -> 'a -> (int * 'a) tree @@ -376,6 +360,8 @@ val insert = fn : (int * 'a) tree -> int -> 'a -> (int * 'a) tree +### Binary Search Trees Excercise + ```sml ... ``` @@ -383,6 +369,9 @@ val insert = fn : (int * 'a) tree -> int -> 'a -> (int * 'a) tree + +### Binary Search Trees Excercise + ```sml fun insert Nil k v = Br ((k, v), Nil, Nil) | insert (Br (node, left, right)) k v = diff --git a/Material/Tutorials/sml/declarations.md b/Material/Tutorials/sml/declarations.md index 54c9618..e456b00 100644 --- a/Material/Tutorials/sml/declarations.md +++ b/Material/Tutorials/sml/declarations.md @@ -1,46 +1,47 @@ # Standard ML -## declarations +## Declarations --- ### Reminder: Values and Types - - -what's a **value**? +What's a **value**? -* integers are values -* a pair of an integer and real is a value -* every function is a value +* Integers are values +* A pair of an integer and real is a value +* Every function is a value -every value in ML has a **type**: +### Reminder: Values and Types + +Every value in ML has a **type**: -* some types are atomic -* some types are builtin -* some types may be both builtin and atomic -* other types are compound (made from smaller types) +* Some types are atomic +* Some types are builtin +* Some types may be both builtin and atomic +* Other types are compound (made from smaller types) -remember: not everything in ML is a value, types are not values! +Remember: not everything in ML is a value, types are not values! -#### where do values come from? (1) +### where do values come from? -computation: during execution, the program generates more and more values +During execution, the program generates more values by using **operators** for **computation** -* example: the elements of the Fibonacci sequence -* operators: create values - * `div` creates values of type int - * (12, 12.3) is a compound value - * `fn ? => ?` creates values which are functions - * `fun x(t) = ...` creates a value which is a fucntion and names it -* a value constructor is always an operator +* `div` creates values of type int +* `fn ? => ?` creates values which are functions +* `fun x(t) = ...` creates a value which is a fucntion and names it -NOTE: -a type constructor is not an operator, it takes types and creates a new type +A value constructor is always an operator + + + +### where do values come from? + +Important Note: a type constructor is not an operator, it takes types and creates a new type * `->` * `*` @@ -48,31 +49,30 @@ a type constructor is not an operator, it takes types and creates a new type -#### where do values come from? (2) +### where do values come from? -initial values introduced by programmer: +Values can also be introduced by the programmer: -* atomic values: every literal is a value -* composite values: expressions, function definitions +* Atomic values: every literal is a value +* Composite values: expressions, function definitions -declarations: +### declarations -* making new values out of previous values and literals -* providing names for this value -* or making new types of previous types (builtin and user defined) -* providing names for these new types +* Making new values out of previous values and literals +* Providing names for this value +* Making new types out of previous types (builtin and user defined) +* Providing names for these new types -REPL (reminder): +### REPL -* programmer types in a new value (expression, function definition) -* ML Engine: - * infers type of value: `int`, `int * real`, `'a -> int -> int`, ... - * computes value (if it is an expression) - * associates the name `it` with this value +For every new value the programmer inserts, expression or declaration, the ML engine: +* Infers type of that value: `int`, `int * real`, `'a -> int -> int`, ... +* Computes the value (if it is an expression) +* Associates the name `it` with this value --- @@ -94,12 +94,12 @@ area 2.0; ### Identifiers in ML * `val` declaration binds a name to a value -* names are not variables! -* a name can not be used to change its value (actually a constant) -* a name can be reused for another purpose - * by scoping rules - * by hiding a name in an outer scope - * by redefinitions to the REPL... +* Names are not variables! +* A name can not be used to change its value (actually a constant) +* A name can be reused for another purpose + * By scoping rules + * By hiding a name in an outer scope + * By redefinitions to the REPL... ```sml val pi = "pi"; @@ -108,14 +108,16 @@ val pi = "pi"; -if a name is declared again the new meaning is adopted afterwards +### Identifiers in ML + +In case a name is declared again, the new meaning is adopted afterwards ```sml pi; ``` -but does not affect existing uses of the name +However, this does not effect existing uses of the name ```sml area 1.0; @@ -134,31 +136,32 @@ area 1.0; ⚠️ when modifying a program, be sure to recompile the entire file -NOTE: redefining indicates that internally, the SML engine is not an interpreter, it actually compiles and links, and does not amend this linking upone - --- -### `val` and `val rec` +### val rec -we can define a function using val +We can define a function using val: ```sml val sq = fn x => x * x; ``` -what about recursive functions? +What about recursive functions? ```sml -fun f(n) = if n=0 then 1 else n * f(n-1); - -val f = fn (n) => if n=0 then 1 else n * ??; +val f = fn (n) => if n=0 then 1 else n * f (n-1); ``` + + + + +### val rec + +In order to do so, we may use `val rec`: ```sml -val rec f = fn (n) => - if n=0 then 1 - else n * f(n-1); +val rec f = fn (n) => if n=0 then 1 else n * f(n-1); ``` @@ -187,7 +190,7 @@ It is always a question when does a binding get into effect ### Pattern Matching -patterns can be used to simplify function definitions +Patterns can be used to simplify function definitions ```sml fun factorial 0 = 1 @@ -195,22 +198,24 @@ fun factorial 0 = 1 ``` -when the function is called, the first pattern to match the argument determines which expression on the right hand side will be evaluated +When the function is called, the first pattern to match the argument determines which expression on the right hand side will be evaluated -* patterns can consist of - * constants - int, real, string, ... - * constructs - tuples, datatype constructors - * variables - all the rest - * underscore - a wildcard -* matching is recursive -* any variable in the pattern is bound to the corresponding value in X -* there is no binding where the wildcard is used +### Pattern Matching + +Patterns can consist of: + * Constants - int, real, string, ... + * Constructs - tuples, datatype constructors + * Variables - all the rest + * Underscore - a wildcard +> Note: matching is recursive -what will be printed? +### Pattern Matching + +What will be printed? ```sml fun foo (x,1) = x @@ -256,16 +261,14 @@ case 7 of ``` -* if `Pi` is the first to match then the result is `Ei` -* no symbol terminates the case expression - * enclose in parentheses to eliminate ambiguity +* If `Pi` is the first to match then the result is `Ei` +* No symbol terminates the case expression - enclose in parentheses to eliminate ambiguity --- ### Type aliasing -* you can give a new name to an existing type -* the new name is only an alias +You can give a new name to an existing type. ```sml type vec = real*real; @@ -276,20 +279,18 @@ fun (x1,y1) ++ (x2,y2) : vec = (x1+x2,y1+y2); (3.6,0.9) ++ (0.1,0.2) ++ (20.0,30.0); ``` - +> Note: the new name is only an alias --- ### Declarations inside an expression +In case you want to limit a scope of a declaration, you may use a `let` expression: ```sml let D in E end ``` ```sml -fun gcd (n, m) = if m=0 then n else gcd(n mod m, m); - -fun fraction (n,d) = - (n div gcd(n,d), d div gcd(n,d)); +fun gcd (n, m) = if m=0 then n else gcd (n mod m, m); fun fraction(n,d)= let @@ -302,6 +303,8 @@ fun fraction(n,d)= +### Declarations inside an expression + `D` may be a compound declaration ```sml @@ -316,6 +319,8 @@ end; +### Declarations inside an expression + `let` can be simulated using anonymous functions ```sml @@ -347,7 +352,9 @@ fun sqroot a = -### `local` +### Declarations inside a Declaration + +We may also use a `local` declaration: ```sml local D1 in D2 end @@ -445,8 +452,8 @@ NOTE: the "body" of `local` may be empty val ID1 = E1 and ... and IDn = En ``` -* evaluates `E1`, ..., `En` -* and only then declares the identifiers `ID1`, ..., `IDn` +* Evaluates `E1`, ..., `En` +* And only then declares the identifiers `ID1`, ..., `IDn` ```sml val x = 3; diff --git a/Material/Tutorials/sml/exam-questions.md b/Material/Tutorials/sml/exam-questions.md index e19a936..88fcfe6 100644 --- a/Material/Tutorials/sml/exam-questions.md +++ b/Material/Tutorials/sml/exam-questions.md @@ -105,7 +105,7 @@ End value: 11 ``` -Output: 1222222222 +Output: 1111111111 End value: 2 ``` diff --git a/Material/Tutorials/sml/exceptions.md b/Material/Tutorials/sml/exceptions.md index 384c1d6..1344d07 100644 --- a/Material/Tutorials/sml/exceptions.md +++ b/Material/Tutorials/sml/exceptions.md @@ -4,10 +4,10 @@ --- -### exceptions - why? +### exceptions - motivation -* an extensive part of code is error handling -* a function can return an answer, or fail to find one, or signal that a solution does not exists +* An extensive part of code is error handling +* A function may return an answer, fail to find one or signal that a solution does not exists @@ -26,21 +26,23 @@ case methodA(problem) of ; ``` -it can be tedious and requires explicit handling +Without exceptions, error handling can be tedious and requires explicit handling. -sometimes we don't really know what to do with the error, so we'll simply return it +Sometimes we don't really know what to do with the error, so we'll simply return it --- -### exceptions +### exceptions usage - key concepts -* when an error is discovered we will **raise** an exception -* the exception will propagate up until someone **handles** it -* the caller of a function doesn't have to check any error values +* When an error is discovered we will **raise** an exception +* The exception will propagate up until someone **handles** it +> The caller of a function doesn't have to check any error values -in pseudo code: +### exceptions usage + +In pseudo code: ```sml fun inner = do_calculation @@ -56,7 +58,7 @@ fun outer = middle(…) handle global_error; ### the exception type `exn` -* we can **raise** only a specific type: `exn` +* In SML we can **raise** only values of specific type: `exn` * `exn` is a special datatype with an **extendable** set of constructors and values ```sml @@ -70,7 +72,9 @@ Problem; -values of type `exn` have all the privileges of other values +### the exception type `exn` + +Values of type `exn` have all the privileges of other values ... ```sml val p = Problem 1; @@ -81,6 +85,7 @@ fun whats_the_problem (Problem p) = p; +### the exception type `exn` ... except ```sml @@ -91,20 +96,22 @@ x = x; --- -### raising exceptions - semantics +### raising exceptions ```sml raise Exp ``` -* the expression `Exp` of type `exn` evaluates to `e` +* Assume the expression `Exp` evaluates to `e` (and is of type `exn`) * `raise Exp` evaluates to an exception packet containing `e` -* packets are not ML values! +> Important note - packets are not ML values! -* exception packets propagate under the call by value rule -* all of the following evaluate to `raise Exp` +### raising exceptions + + +All of the following "evaluate" to `raise Exp` ```sml f (raise Exp) @@ -113,7 +120,7 @@ f (raise Exp) raise (Exp1 (raise Exp)) (* Exp1 is a constructor *) -(raise Exp, raise Exp1) (* or {a=Exp, b=Exp1} *) +(raise Exp, raise Exp2) (* or {a=raise Exp, b=raise Exp2} *) let val name = raise Exp in some_expression end @@ -146,8 +153,8 @@ Exp_0 handle | Pn => Exp_n ``` -* all `Exp_i`s must be type-compatible -* all `Pi`s must be valid patterns for the type `exn` +* All `Exp_i`s must be type-compatible +* All `Pi`s must be valid patterns for the type `exn` ```sml fun len l = 1 + len (tl l) handle Empty => 0; @@ -162,19 +169,21 @@ fun len l = 1 + len (tl l) handle Empty => 0; Exp_0 handle Cons1 x => Exp_1 ``` -* assume `Exp_0` evaluates to some value `V` then the value of this expressions is - * `Exp_1` if `Exp_0` evaluates to `raise Cons1 x` - * `V` otherwise (`V` may be either a normal value or a non-matching raised exception) -* `handle` is short-circuiting -* exactly equivalent to familiar notions from C++ +* Assume `Exp_0` evaluates to some `V` (which is either a value or an exception packet), then the expression evaluates to: + * `Exp_1` - in case `V` is `raise Cons1 x` + * `V` - otherwise (`V` may be either a normal value or a non-matching raised exception) + +> `handle` is short-circuiting + +> Exactly equivalent to familiar notions from C++ --- ### the type of `raise Exp` -* the expression `raise Exp` is of type `'a` -* it is **not** an expression of type `exn` -* it simply puts no restrictions on other parts of the expression +* The expression `raise Exp` is of type `'a` +* It is **not** an expression of type `exn` +* This is necessary to avoid restricting the other parts of the expression. ```sml fun throw _ = raise Empty; diff --git a/Material/Tutorials/sml/functions.md b/Material/Tutorials/sml/functions.md index 550761d..15f57a8 100644 --- a/Material/Tutorials/sml/functions.md +++ b/Material/Tutorials/sml/functions.md @@ -6,14 +6,14 @@ ### Side note - operators -A function of two arguments can be treated as an infix operator +A function of two arguments can be treated as an infix operator: ```sml fun d (x,y) = Math.sqrt (x*x + y*y); ``` -Convert to an infix operator +Convert to an infix operator using the `infix` operator: ```sml infix d; @@ -26,19 +26,19 @@ infix d; -the infix declaration can come __before__ the function definition +### operators + +The infix declaration can come __before__ the function definition: ```sml infix d; fun (x d y) = Math.sqrt(x*x + y*y); - -d; ``` -```sml -op d; +To treat an operator as a prefix function use the `op` keyword: +```sml op d(1.0,3.0); ``` @@ -49,14 +49,14 @@ op d(1.0,3.0); Curry = Haskell Curry (1900-1982), who invented the "trick" -any function of two arguments `$(\alpha * \beta)\rightarrow \gamma$` can be expressed as a **curried** function of one argument `$\alpha\rightarrow (\beta \rightarrow \gamma)$` +Any function of two arguments `$(\alpha * \beta)\rightarrow \gamma$` can be expressed as a **curried** function of one argument `$\alpha\rightarrow (\beta \rightarrow \gamma)$` ```sml fun prefix (pre, post) = pre ^ post; ``` -the curried version +The curried version ```sml fun prefix pre = fn post => pre^post; @@ -77,9 +77,9 @@ val prefix = fn : string -> (string -> string) ### partial application -AKA=partial evaluation +AKA - Partial Evaluation -You don't have to provide subsequent arguments +You don't have to provide subsequent arguments: ```sml prefix "Dr. "; @@ -88,7 +88,7 @@ it "Watson"; ``` -as always, functions are values +As always, functions are values: ```sml val doctorify = prefix "Dr. "; @@ -99,14 +99,14 @@ doctorify "Jekyll"; --- -### currying = syntactic sugar +### currying - syntactic sugar ```sml fun prefix pre post = pre ^ post; ``` -is equivalent to +is equivalent to: ```sml fun prefix pre = fn post => pre ^ post; @@ -124,7 +124,7 @@ prefix "Dr. " "Watson"; ``` -the rule is: +The rule is: * a function invocation `F E1 E2 ... En` * abbreviates `(...((F E1) E2)...) En` @@ -133,7 +133,7 @@ the rule is: ### Example of partial application -applying infix operator only to one operand +Applying infix operator only to one operand ```sml fun add5 y = op+ (5, y); @@ -145,7 +145,7 @@ fun mul5 y = op* (5, y); - +### Example of partial application Now, generalize the operator and operand ```sml @@ -198,7 +198,7 @@ it #"1"; -what will be printed? +What will be printed? ```sml fun f1 a b = f1 a b; @@ -207,7 +207,7 @@ fun f1 a b = f1 a b; -what will be printed? +What will be printed? ```sml fun f2 g x = g (f2 g) x; @@ -216,7 +216,7 @@ fun f2 g x = g (f2 g) x; -what will be printed? +What will be printed? ```sml fun f3 x y z = (x, x(y), y(z)); @@ -225,7 +225,7 @@ fun f3 x y z = (x, x(y), y(z)); -what will be printed? +What will be printed? ```sml fun f4 f = f f4; diff --git a/Material/Tutorials/sml/introduction.md b/Material/Tutorials/sml/introduction.md index 0be4dad..b3da165 100644 --- a/Material/Tutorials/sml/introduction.md +++ b/Material/Tutorials/sml/introduction.md @@ -6,36 +6,34 @@ ### What's SML? -born 1985. named Standard ML = Meta Language... - -* the 1970s, Robin Milner and group working at Edinburgh University on "LCF" (a theorem proover) -* ML invented as an embedded scripting language of LCF -* many ad-hoc independent implementations, many new ideas -* 1997: first real standard +* SML - Standard Meta Language +* *General-purpose*, *modular*, *statically typed*, *functional* programming language. +* Proposed in 1983, first stable implementation in 1997. +* A (relatively) modern dialect of ML, which was invented in 1973. ### Why SML? -* exemplar of functional programming - * functional = forget about variables - * functional = functions are values - * functional = higher lever functions - * ... -* exemplar of **type safe** functional programming - * strongly typed: no type error can go undetected - * statically typed: all type errors are detected at compile time -* influenced: Haskell, OCaml, Scala, F#, ... +* Exemplar of functional programming - i.e.: + * Forget about variables (kind of) and loops. + * Functions are first-class citizens. + * Data is immutable (again, kind of) - -### More on SML +* Exemplar of **type safe** language + * Strongly typed: no type error can go undetected + * Statically typed: all type errors are detected at compile time + +* Influenced: Haskell, OCaml, Scala, F#, Python ... + + -main applications: +### Use cases of SML -* research -* teaching -* few industrial applications +* Research +* Teaching +* Few industrial applications @@ -47,22 +45,21 @@ Robin Milner on ML: -two main features emphasized by the language creator - -* exception mechanism for disciplined management of errors -* type system - * flexibility of typeless - * safety of typed +### Language Spirit ---- +Two main features emphasized by the language creator: -### Running +* Exception mechanism for disciplined management of errors +* Type system + * Flexibility of typeless + * Safety of typed -mode of operation +--- -* REPL (sort of; it is not interpreted) -* first prompt (-) and secondary prompt (=) -* semicolon terminated +## Executing SML code +We will mostly execute in REPL mode. +* First prompt (-) and secondary prompt (=) +* Expressions followed by a semicolon yield a response ```text Standard ML of New Jersey v110.79 [built: Sat Oct 26 12:27:04 2019] @@ -72,8 +69,9 @@ val it = 10 : int -* ML is usually used in a REPL -* expressions followed by a semicolon yield a response +### Executing SML code + +For example: ```sml 2 + 2; @@ -82,22 +80,13 @@ val it = 10 : int --- -### Naming Values - - +## Values -naming values +We use the `val` keyword to name a new value: ```sml -val seconds = 60; -``` - - - +val seconds : int = 60; -using named values in expressions - -```sml val minutes = 60; val hours = 24; @@ -106,9 +95,13 @@ seconds * minutes * hours; ``` +Important note - these are **values**, not **variables** + -the identifier `it` +### Values + +We use the `it` identifier in order the refer the last response: ```sml seconds * minutes * hours; @@ -121,41 +114,41 @@ val secs_in_hour = it; --- -### Identifiers in SML +## Identifiers -two kinds of identifiers: +There are two kinds of identifiers in SML: -* alphabetical identifiers, as found in most languages -* special identifiers, mainly for defining new operators +* Alphabetical identifiers, as found in most languages. +* Special identifiers - mainly for defining new operators. -use of identifiers: +### Identifiers +With identifiers we denote: -* names of types -* names of values - * ordinary values - * functions +* Names of types +* Names of values (ordinary values and functions) -identifier congestion? +### Identifiers +Identifier congestion? -* scoping -* hiding -* no overloading! +* Scoping +* Hiding +* No overloading! ### Alphabetical Identifiers -* begin with a letter -* followed by a sequence of letters, digits, `_`, or `'` -* case sensitive -* some alphabetical identifiers are reserved words - * names of operators: `and`, `if`, `then`, `else`, `orelse`, ... - * punctuations: `fun`, `let`, `local`, `of`, ... - * many more +* Begin with a letter +* Followed by a sequence of letters, digits, `_`, or `'` +* Case sensitive +* Some alphabetical identifiers are reserved words + * Names of operators: `and`, `if`, `then`, `else`, `orelse`, ... + * Punctuations: `fun`, `let`, `local`, `of`, ... + * Many more ```sml x @@ -169,11 +162,11 @@ h''3_H ### Special Identifiers -are called "symbolic" identifiers in the SML lingo +These are called "symbolic" identifiers in the SML lingo. -* a sequence of one or more of the following ``! % & $ # + - * / : < = > ? @ \ ~ \ ^ | ` `` -* should not be one of: `: | = => -> #` -* allowed wherever an alphabetic name is +* A sequence of one or more of the following ``! % & $ # + - * / : < = > ? @ \ ~ \ ^ | ` `` +* Should not be one of: `: | = => -> #` +* Allowed wherever an alphabetic name is ```sml val +-+-+ = 1415; @@ -187,30 +180,26 @@ are called "symbolic" identifiers in the SML lingo `abstype` | `and` | `andalso` | `as` | `case` | `datatype` | `do` | `else` | `end` | `eqtype` | `exception` | `fn` | `fun` | `functor` | `handle` | `if` | `in` | `include` | `infix` | `infixr` | `let` | `local` | `nonfix` | `of` | `op` | `open` | `orelse` | `raise` | `rec` | `sharing` | `sig` | `signature` | `struct` | `structure` | `then` | `type` | `val` | `while` | `with` | `withtype` -keywords are words that look like identifiers, but cannot be used as such; they are reserved for other purposes. +Keywords are words that look like identifiers, but cannot be used as such; they are reserved for other purposes. --- -### The Basic Types of ML +## TYPES -six "basic" types: `int`, `real`, `string`, `char`, `bool`, `unit` +There are six "basic" types in sml: `int`, `real`, `string`, `char`, `bool`, `unit` -* they are basic, since they are *atomic* -* they are basic, since they are *builtin* -* there are other, user defined types: - * user defined types can be atomic - * user defined types can be compound - * a programming language may furnish the user with some builtin, non-atomic types (not in SML) +* These are basic since they are *atomic* and *builtin* +* There are other, user defined types which may be atomic or compound. +## primitive types + `int`, `real`, `string`, `char`, `bool`, `unit` -* are valid identifiers -* are not reserved identifiers -* are not keywords -* are predefined identifiers, naming the builtin types -* the identifiers may be used for other purposes; the underlying type may remain nameless +* These are valid identifiers and not reserved keywords! +* Technically these are predefined identifiers, naming the builtin types +* The identifiers may be used for other purposes; the underlying type may remain nameless ```sml val int = 6; @@ -221,11 +210,9 @@ real * int - real; --- -### int - -#### literals +### int literals -* sequence of digits +* Sequence of digits * 0 * 01234 * `~` for a unary minus sign @@ -233,10 +220,10 @@ real * int - real; * `~85601435654638` +#### int infix operators +`+` `-` `*` `div` `mod` -infix operations: `+` `-` `*` `div` `mod` - -conventional precedence (parenthesis can be dropped without change of meaning) +Conventional precedence (parenthesis can be dropped without change of meaning) ```sml (((m * n) * l) - (m div j)) + j @@ -244,60 +231,58 @@ conventional precedence (parenthesis can be dropped without change of meaning) --- -### real - -#### literal +### real literals -* decimal point +* Decimal point * `0.01` * `2.718281828` -* e notation +* "e" notation * `7E~5` * `~1.2E12` * `~123.4E~2` is the same as `~1.234` -infix operators: `+` `-` `*` `/` +### real infix operators -NOTE: note that `+`, `-`, `*` are overloaded +`+` `-` `*` `/` + + * Note that `+`, `-`, `*` are overloaded -#### functions +### numeric types functions * `floor` converts `real` to `int` * `real` converts `int` to `real` * `sqrt`, `sin`, `cos`, `tan`, `exp`, `ln` all of type `real->real` -* all need the `Math.` prefix: `Math.sqrt` +* All need the `Math.` prefix: `Math.sqrt` --- ### strings - - -string literals are written in double quotes +String literals are written in double quotes ```sml "ML is the best"; ``` -escape sequences: `\n`, `\t`, `\"`, `\\` +Escape sequences: `\n`, `\t`, `\"`, `\\` -concatenation operator +### string operators + +Concatenation: ```sml "Standard" ^ " ML"; ``` - - -comparison of strings +comparison of strings: ```sml "abc" < "cba"; @@ -308,6 +293,8 @@ comparison of strings +### string operators + `size` returns the number of characters ```sml @@ -315,15 +302,13 @@ size "SML"; ``` -notice the syntax of function application - no parentheses! +Notice the syntax of function application - no parentheses! --- ### Characters - - -characters (values of type `char`) are distinguished from strings of length 1 by the character `#` +Characters (values of type `char`) are distinguished from strings of length 1 by the character `#` ```sml "0"; @@ -334,17 +319,17 @@ characters (values of type `char`) are distinguished from strings of length 1 by +### Char operration + conversion between strings and chars ```sml str #"0"; -String.sub("hello", 0); +String.sub ("hello", 0); ``` - - conversion between chars and ASCII ```sml @@ -358,7 +343,7 @@ chr it; ### boolean -two named values: `true` and `false` +Two named values: `true` and `false` ```sml true; @@ -369,19 +354,19 @@ false; ``` -he names `true` and `false` are not keywords; they are predefined identifiers +The names `true` and `false` are not keywords; they are predefined identifiers! --- -### tuples - cartesian product type +### tuples - Cartesian product type -* the n-tuple whose components are `x1`, ..., `xn`: +* N-tuple whose components are `x1`, ..., `xn`: ```sml (x1, x2, ..., xn) ``` -* the components can be of any type, including tuples +* The components can be of any type, including tuples ```sml val a = (1.5, 6.8); @@ -396,39 +381,38 @@ val a = (1.5, 6.8); ### records -* records have components (fields) identified by name +* Records have components (fields) identified by name ```sml val me = { name="Ofir", age=30 }; ``` -* type lists each field as `label : type` -* enclosed in braces `{...}` +* Type lists each field as `label : type` - -* selecting a field +### records +* Selecting a field ```sml #name me; ``` -* tuples **can be seen as** records with numbers as implicit field labels +* Tuples **can be seen as** records with numbers as implicit field labels ```sml #2 ("one", "two", "three"); ``` -* note that the numbering starts with 1 +* Note that the numbering starts with 1 --- ### lists -a list is a finite sequence of elements +lists are finite sequences of elements: ```sml [3, 5, 9]; @@ -439,61 +423,70 @@ a list is a finite sequence of elements -elements may appear more than once +### lists + +Elements may have any type but all elements must have the same type ```sml -[3,4,3]; +[(1, "one"), (2, "two")] : (int*string) list; +[[3.1], [], [5.7, ~0.6]] : (real list) list; ``` - - -elements may have any type but all elements must have the same type +--- +### If-else expressions ```sml -[(1, "one"), (2, "two")] : (int*string) list; -[[3.1], [], [5.7, ~0.6]] : (real list) list; +val x = 1; +val y = 2; +if x < y then x*x else 0; ``` + +* *if-else* is not a control structure, but an expression! (a bit like trinary operators in C). +* The two possible outcomes must be of the same type. +* *else* is mandatory. --- -### mapping - functions +### Mapping - functions ```sml -fun sq(x: int) = x*x; +fun sq (x: int) = x*x; ``` -* keyword `fun` starts the function declaration +* `fun` keyword starts the function declaration * `sq` is the function name * `x:int` is the formal parameter with type constraint * `x*x` is the body and it is an **expression** -* the result of the function is the result of evaluating the **expression** of the function body with the actual parameter -* `int->int` is the standard mathematical notation for a function type that takes an integer and returns an integer +### Functions + +* The result of the function is the result of evaluating the **expression** of the function body with the actual parameter. +* `int->int` is the standard mathematical notation for a function type that takes an integer and returns an integer. --- -### applying a function +### Applying a Function -* simple function call +* Simple function call ```sml (sq 3); ``` -* when a function is called the argument is evaluated and then passed to the function +* When a function is called the argument is evaluated and then passed to the function ```sml sq (sq 3); ``` -* the parentheses are optional +* Parentheses are optional ```sml sq 3; @@ -502,7 +495,7 @@ fun sq(x: int) = x*x; -* parentheses are also optional in function definitions +* Parentheses are also optional in function definitions ```sml fun sq x:int = x*x; @@ -511,16 +504,16 @@ fun sq(x: int) = x*x; --- -### arguments and results +### Arguments and Results -* every function has one argument and one result -* any type can be passed/returned!!! -* tuples are used to pass/return several values +* Every function has one argument and one result +* Any type can be passed/returned!!! +* Tuples are used to pass/return several values ```sml val a = (3.0, 4.0); -fun lengthvec (x:real, y:real) = Math.sqrt(x*x + y*y); +fun lengthvec (x:real, y:real) = Math.sqrt (x*x + y*y); lengthvec a; @@ -530,9 +523,9 @@ lengthvec (5.0, 12.0); --- -### functions as values +### Functions as Values -anonymous functions with `fn` notation +Anonymous functions with `fn` notation ```sml fn x:int => x*x; @@ -541,9 +534,8 @@ it 3; ``` - -the following declarations are identical +The following declarations are identical ```sml fun sq x:int = x*x; @@ -553,17 +545,17 @@ val sq = fn x:int => x*x; --- -### returning functions +### Returning a Function as Value -* functions can also be __returned__ from other functions +* Functions can also be __returned__ from other functions ```sml - fun inttwice(f: (int->int)) = + fun inttwice (f: int->int) = fn x => f (f x); ``` -* the arrow is right associative so the type of `inttwice` is equivalent to: +* The arrow is right associative so the type of `inttwice` is equivalent to: ```sml val inttwice = fn : (int -> int) -> (int -> int) @@ -571,7 +563,9 @@ val sq = fn x:int => x*x; -example +### returning functions + +For example: ```sml inttwice (fn x => x*x); @@ -582,7 +576,7 @@ it 3; --- -### type inference +### Type Inference ML deduces the types in expressions @@ -593,67 +587,67 @@ fun facti (n, p) = ``` -* literals `0` and `1` have type `int`. -* therefore `n=0` and `n-1` involve integers so `n` has type `int` -* `n*p` must be integer multiplication, so `p` has type `int` -* `facti` returns type `int` +* Literals `0` and `1` have type `int`. +* Therefore `n=0` and `n-1` involve integers so `n` has type `int` +* `n*p` must be integer multiplication, so `p` is of type `int` +* Therefore `facti` returns type `int` --- -### type constraints +### Type Constraints -* certain functions are overloaded, e.g. `abs`, `+`, `-`, `~`, `*`, `<` -* the type of an overloaded function is determined from context, or is set to `int` by default -* types can be stated explicitly +* Certain functions are overloaded, e.g. `abs`, `+`, `-`, `~`, `*`, `<` +* The type of an overloaded function is determined from context, or is set to `int` by default +* Types can be stated explicitly --- +### Type Inference Exercise what will be printed for the following definitions of `min`? ```sml -fun min(x, y) = if x < y then x else y; +fun min (x, y) = if x < y then x else y; ``` ```sml -fun min(x:real,y) = if x < y then x else y; +fun min (x:real, y) = if x < y then x else y; ``` ```sml -fun min(x:string,y) = if x < y then x else y; +fun min (x:string, y) = if x < y then x else y; ``` ```sml -fun min(x,y):real = if x < y then x else y; +fun min (x,y) : real = if x < y then x else y; ``` ```sml -fun min(x,y) = if x < y then x:real else y; +fun min (x,y) = if x < y then x:real else y; ``` ---- - + write a function `foo` such that its type is: ```sml val foo = fn : int * real -> real ``` -but you can't use type annotations +without using type annotations ```sml ... @@ -662,24 +656,24 @@ but you can't use type annotations --- -### polymorphic type checking +### Polymorphic Type Checking | | | flexibility | security | -|:-------------------------:|:------:|:-----------:|:--------:| -| weakly typed | lisp | ✔ | | -| strongly typed* | Pascal | | ✔ | -| polymorphic type checking | ML | ✔ | ✔ | +| :-----------------------: | :----: | :---------: | :------: | +| Weakly typed | Lisp | ✔ | | +| Strongly typed* | Pascal | | ✔ | +| Polymorphic type checking | ML | ✔ | ✔ | -and in ML most types are inferred automatically 😎 +As a bonus - in ML most types are inferred automatically 😎 NOTE: Pascal has type punning so its not as strongly typed as ML --- -### polymorphic function definitions +### Polymorphic Function Definitions -* if type inference leaves some types completely unconstrained then the definition is polymorphic -* a polymorphic type contains a type variable, e.g. 'a, 'b +* In case type inference leaves some types completely unconstrained then the definition is polymorphic. +* A polymorphic type contains a type variable, e.g. 'a, 'b ```sml fun pairself x = (x, x); @@ -695,37 +689,39 @@ fun pair (x,y) = (y, x); ### functions as values - the polymorphic case - -what will be printed? +### Polymorphic Functions Exercise +What will be printed? ```sml -fun twice f = fn x => f (f x); +fun ident x = x; ``` - -what will be printed? +### Polymorphic Functions Exercise +What will be printed? ```sml -fun ident x = x; +fun twice f = fn x => f (f x); ``` -what will be printed? +### Polymorphic Functions Exercise +What will be printed? ```sml fun twice f = fn x => f (f x); twice (fn x => x * x); -it(2); +it 2; ``` -sometimes ML gives us a hard time +### Polymorfic functions Excercise +Sometimes ML gives us a hard time.. ```sml fun twice f = fn x => f (f x); @@ -734,20 +730,20 @@ twice ident; ``` -you usually may ignore it. or use a workaround: +You usually may ignore it. or use a workaround: ```sml -fn x => (twice ident) (x); +fn x => twice ident (x); ``` --- -### functional vs. imperative +### Functional vs. Imperative Programming -* imperative - using commands to change the state -* functional - stateless. using expressions recursively to calculate the result -* example: Euclid's algorithm for the Greatest Common Divisor (GCD) of two natural numbers: +* Imperative - using commands to change the state +* Functional - stateless. using expressions recursively to calculate the result +* Example: Euclid's algorithm for the Greatest Common Divisor (GCD) of two natural numbers: $$gcd(m,n) = \begin{cases}n,m = 0&\\gcd(n mod m,m), m>0\end{cases}$$ @@ -755,7 +751,7 @@ $$gcd(m,n) = \begin{cases}n,m = 0&\\gcd(n mod m,m), m>0\end{cases}$$ ### GCD - Python vs. ML -an imperative Python program: +An imperative Python program: ```python def gcd(m: int, n: int) -> int: @@ -764,10 +760,10 @@ def gcd(m: int, n: int) -> int: return n ``` -a functional program in Standard ML: +A functional program in Standard ML: ```sml -fun gcd(m,n) = if m=0 then n else gcd(n mod m, m); +fun gcd (m,n) = if m=0 then n else gcd (n mod m, m); ``` -which one is more efficient? 🧐 +Which one is more efficient? 🧐 diff --git a/Material/Tutorials/sml/lists.md b/Material/Tutorials/sml/lists.md index 06dfec3..049799e 100644 --- a/Material/Tutorials/sml/lists.md +++ b/Material/Tutorials/sml/lists.md @@ -1,10 +1,12 @@ # Standard ML -## lists +## Lists --- -a list is an __immutable__ finite sequence of elements +### Lists + +Lists are __immutable__ finite sequences of elements ```sml [3, 5, 9]: int list @@ -14,14 +16,16 @@ a list is an __immutable__ finite sequence of elements -order matters +### Lists + +Order matters ```sml [1, 2, 3] <> [3, 2, 1]; ``` -and repetitions count +And repetitions count ```sml [3, 3, 3] <> [3]; @@ -30,7 +34,9 @@ and repetitions count -elements may have any type +### Lists + +Elements may have any type ```sml [(1,"One"),(2,"Two")] : (int*string) list @@ -46,7 +52,9 @@ elements may have any type -the empty list has a polymorphic type +### Lists + +The empty list has a polymorphic type ```sml []: 'a list @@ -61,15 +69,17 @@ nil; --- -### building a list +### Building lists -a list is either *empty* or *a head followed by a tail* +A list is either *empty* or *a head followed by a tail* `[1,2,3]` ➭ head: `1` tail: `[2,3]` -use the infix operator `::` (aka `cons`) to build a list +### building lists + +Use the infix operator `::` (aka `cons`) to build a list ```sml 1 :: [2, 3]; @@ -80,6 +90,8 @@ use the infix operator `::` (aka `cons`) to build a list +### building lists + `::` associates to the right, so `x1 :: x2 :: ... :: xn :: nil` @@ -90,21 +102,26 @@ use the infix operator `::` (aka `cons`) to build a list + +### building lists + `::` is a *constructor* so it can be used in patterns ```sml fun replace_head (_::t) x = x :: t - | replace_head [] _ = [] -; + | replace_head [] _ = []; ``` --- -### builtin fundamental functions +## builtin fundamental functions + +### builtin fundamental functions + `null` - tests whether a list is empty ```sml @@ -115,6 +132,8 @@ fun null [] = true +### builtin fundamental functions + `hd` - evaluates to the head of a non-empty list ```sml @@ -124,6 +143,9 @@ fun hd (x::_) = x; + +### builtin fundamental functions + ```sml hd[ [ [1,2], [3] ], [ [4] ] ]; ``` @@ -141,6 +163,8 @@ hd it; +### builtin fundamental functions + `tl` - evaluates to the tail of a non-empty list ```sml @@ -150,6 +174,9 @@ fun tl (_::xs) = xs; + +### builtin fundamental functions + ```sml tl ["how", "are", "you?"]; ``` @@ -185,6 +212,8 @@ range (2, 5); +### example - building a list of integers + ```sml infix --; val op-- = range; @@ -242,10 +271,12 @@ drop (0, [6,5,4]) --- -### tail recursion +## tail recursion +### recursion types + normal recursion ```sml @@ -272,7 +303,7 @@ fun length [] = 0 ``` -use an **accumulator** to make it iterative +Use an **accumulator** to make it iterative ```sml local @@ -299,12 +330,12 @@ fun [] @ ys = ys ``` -* is it tail recursive? -* why can't it be used in patterns? +* Is it tail recursive? +* Why can't it be used in patterns? --- -### side note - `orelse` and `andalso` +## side note - `orelse` and `andalso` they are short-circuiting boolean operators @@ -316,8 +347,10 @@ B1 orelse B2 = if B1 then true else B2; +### `orelse` and `andalso` + ```sml -fun even n = (n mod 2 = 0); +fun even n = (n mod 2 = 0); fun powoftwo n = (n=1) orelse @@ -325,7 +358,7 @@ fun powoftwo n = ``` -is `powoftwo` tail-recursive? +Is `powoftwo` tail-recursive? --- @@ -346,9 +379,9 @@ sqlist [1,2,3]; -transposing a matrix using `map` +### builtin function `map` -![transp gif](../imgs/matrix_transpose.gif) +Transposing a matrix using `map`: ```sml fun transp ([]::_) = [] @@ -392,6 +425,8 @@ val a = [(5,3), (2,1), (7,0)]: polynomial; +### polynomial example + taking the derivative of a polynomial ```sml @@ -418,11 +453,11 @@ fun find f [] = NONE ``` -bound as `List.find` +Bound as `List.find` --- -### `foldl` and `foldr` +## `foldl` and `foldr` @@ -452,7 +487,7 @@ calculates `$[x_1, x_2, … ,x_n] \rightarrow f(x1, … ,f(xn-1, f(xn,init)))$` ### using `foldl` and `foldr` -let's redefine some functions... +Let's redefine some functions... ```sml fun sum l = foldl op+ 0 l; @@ -465,7 +500,7 @@ fun xs @ ys = foldr op:: ys xs; --- -### `exists` and `all` +## `exists` and `all` @@ -477,14 +512,14 @@ fun exists p [] = false ``` -checks if the predicate `p` is satisfied by at least one element of the list +Checks if the predicate `p` is satisfied by at least one element of the list ```sml exists (fn x => x < 0) [1, 2, ~3, 4]; ``` -bound as `List.exists` +Bound as `List.exists` @@ -496,17 +531,20 @@ fun all p [] = true ``` -checks if the predicate `p` is satisfied by **all** elements of the list +Checks if the predicate `p` is satisfied by **all** elements of the list ```sml all (fn x => x >= 0) [1, 2, ~3, 4]; ``` -bound as `List.all` +Bound as `List.all` +### `all` example + + ```sml fun disjoint (xs, ys) = all (fn x => all (fn y => x<>y) ys) xs; @@ -517,15 +555,16 @@ fun disjoint (xs, ys) = ### equality in polymorphic functions -equality is polymorphic in a restricted sense +Equality is polymorphic in a restricted sense -* defined for values constructed of integers, strings, booleans, chars, tuples, lists and datatypes -* not defined for values containing - * functions: equality is undecidable (halting problem) - * reals, because e.g. nan != nan - * elements of abstract types +* Defined for values constructed of integers, strings, booleans, chars, tuples, lists and datatypes +* Not defined for values containing + * Functions: equality is undecidable (halting problem) + * Reals, because e.g. nan != nan + * Elements of abstract types +### equality types ML has a polymorphic equality type `''a` @@ -534,7 +573,7 @@ op=; ``` -somewhat like an interface/trait in other languages +Somewhat like an interface/trait in other languages --- @@ -544,7 +583,7 @@ somewhat like an interface/trait in other languages #### exercise 1 -implement `map` using `foldl` +Implement `map` using `foldl` ```sml val foldl = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b; @@ -607,7 +646,9 @@ fun f o g = fn x => f (g x); -what will be printed? +#### exercise 3a + +What will be printed? ```sml val a = map (upto 2) (upto 2 5); @@ -616,7 +657,8 @@ val a = map (upto 2) (upto 2 5); -what will be printed? +#### exercise 3b +What will be printed? ```sml map @@ -632,7 +674,8 @@ map -what will be printed? +#### exercise 3c +What will be printed? ```sml map @@ -646,7 +689,7 @@ map #### exercise 4 -implement a tail recursive `append` +Implement a tail recursive `append` **reminder**: @@ -658,6 +701,8 @@ fun [] @ ys = ys +#### exercise 4 + ```sml fun append ... ``` @@ -676,7 +721,7 @@ fun append (xs, ys) = aux (aux (xs, []), ys); #### exercise 5 -implement `flatten` using `foldr` +Implement `flatten` using `foldr` ```sml flatten : 'a list list -> 'a list; diff --git a/Material/Tutorials/sml/lists_solutions.md b/Material/Tutorials/sml/lists_solutions.md new file mode 100644 index 0000000..6e10796 --- /dev/null +++ b/Material/Tutorials/sml/lists_solutions.md @@ -0,0 +1,269 @@ +# Standard ML + +## Lists Exam Questions +#### (and solutions) + +--- + +#### exercise 1 + +Implement `map` using `foldl` + +```sml +val foldl = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b; +val map = fn : ('a -> 'b) -> 'a list -> 'b list; +``` + +```sml +fun map f inpList = foldl + _ + _ + inpList +; +``` + + +```sml +map (fn x => x * 2) [1,2,3,4]; +``` + + + +#### exercise 1 - solution + +```sml +fun map f inpList = foldl + (fn (v, l) => l @ [f v]) + [] + inpList +; +``` + + +```sml +map (fn x => x * 2) [1,2,3,4]; +(* val it = [2,4,6,8] : int list *) +``` + + + +#### exercise 2 + +`insSort` (insertion sort) sorts a list according to a given less-then function. + +```sml +val foldr = fn : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b; +val insSort : ('a * 'a -> bool) -> 'a list -> 'a list; +``` + +```sml +fun insSort lt inpList = foldr + _ + _ + inpList +; +``` + + +```sml +insSort (op<) [1, ~3, 5, 0]; +``` + + + + +#### exercise 2 - solution + +```sml +fun insSort lt inpList = foldr + (let fun ins v [] = [v] + | ins v (x::xs) = if lt (v, x) then v::x::xs else x::(ins v xs) + in (fn (a, l) => ins a l) end) + [] + inpList +; +``` + + +```sml +insSort (op<) [1, ~3, 5, 0]; +(* val it = [~3,0,1,5] : int list *) +``` + + + + +#### exercise 3 + +Let's define: + +```sml +fun upto m n = if (m > n) + then [] + else m::(upto (m+1) n) +; + +infix o; +fun f o g = fn x => f (g x); +``` + + + + +#### exercise 3a + +What will be printed? + +```sml +val a = map (upto 2) (upto 2 5); +``` + + + + +#### exercise 3a - solution + +```sml +val a = map (upto 2) (upto 2 5); +(* val a = [[2],[2,3],[2,3,4],[2,3,4,5]] : int list list *) +``` + + + + +#### exercise 3b +What will be printed? + +```sml +map + ( + (fn f => null (f())) + o + (fn t => fn () => tl t) + ) + a +; +``` + + + +#### exercise 3b - solution + +```sml +map + ( + (fn f => null (f())) + o + (fn t => fn () => tl t) + ) + a +; +(* val it = [true,false,false,false] : bool list *) +``` + + + + +#### exercise 3c +What will be printed? + +```sml +map + (List.filter (fn t => t mod 2 = 0)) + a +; +``` + + + + +#### exercise 3c - solution + +```sml +map + (List.filter (fn t => t mod 2 = 0)) + a +; +(* val it = [[2],[2],[2,4],[2,4]] : int list list *) +``` + + + + +#### exercise 4 + +Implement a tail recursive `append` + +```sml +fun append ... +``` + + +```sml +append ([1,2,3], [4,5,6]); +``` + +**reminder**: + +```sml +infix @; +fun [] @ ys = ys + | (x::xs) @ ys = x :: (xs @ ys); +``` + + +#### exercise 4 - solution + +```sml +local + fun aux([], ys) = ys + | aux(x::xs, ys) = aux (xs, x::ys) +in + fun append (xs, ys) = aux (aux (xs, []), ys) +end; +``` + + + +```sml +append ([1,2,3], [4,5,6]); +(* val it = [1,2,3,4,5,6] : int list *) +``` + + + + + +#### exercise 5 + +Implement `flatten` using `foldr` + +```sml +flatten : 'a list list -> 'a list; +``` + +```sml +fun flatten ... +``` + + +```sml +flatten [[1,2,3],[4,5,6],[],[7,8,9]]; +``` + + + + +#### exercise 5 - solution + +```sml +fun flatten xs = foldr (op@) [] xs; +``` + + +```sml +flatten [[1,2,3],[4,5,6],[],[7,8,9]]; +(* val it = [1,2,3,4,5,6,7,8,9] : int list *) +``` + + diff --git a/Material/Tutorials/sml/modules.md b/Material/Tutorials/sml/modules.md index 70365fe..8afa818 100644 --- a/Material/Tutorials/sml/modules.md +++ b/Material/Tutorials/sml/modules.md @@ -4,7 +4,9 @@ --- -a structure is a collection of bindings +### structures + +A structure is a collection of bindings ```sml structure MyModule = struct @@ -12,9 +14,15 @@ structure MyModule = struct end ``` +These are our modules in SML, or better considered as module implementations. + +> Somewhat like a namespace in c++; + -a module can contain any kind of binding +### structures + +A module may contain any kind of binding: ```sml structure MyModule = struct @@ -28,7 +36,9 @@ end; -outside a module refer to a binding from another module by: +### modules usage + +Outside a module, we may refer to a binding like so: ```sml MyModule.answer; @@ -37,20 +47,22 @@ MyModule.answer; +### modules usage + ```sml open MyModule; answer; ``` -* used to get direct access to the bindings of a module -* considered bad style +* Used to get direct access to the bindings of a module (kind of like `using namespace` in C++) +* Considered bad style --- ### signatures -a signature is a type for a module +A signature includes the types of the bindings in a module ```sml signature SIGNAME = sig @@ -62,8 +74,12 @@ structure ModuleName :> SIGNAME = struct end ``` +> Somewhat like a header file in C++ + +### signatures + ```sml signature MATHLIB = sig val pi: real @@ -79,7 +95,9 @@ end; -a module will not type-check unless it matches the signature +### signatures + +When a structure is matched to a signature, the compiler checks that it satisfies the signature requirements: ```sml signature CONSTANTS = sig @@ -89,8 +107,6 @@ end; ``` - - ```sml structure MathConstants :> CONSTANTS = struct val pi = 3.14 @@ -98,8 +114,6 @@ end; (*ERROR*) ``` - - ```sml structure MathConstants :> CONSTANTS = struct val pi = 3 @@ -116,16 +130,26 @@ end; (*ERROR*) structure Foo :> BAR ``` -* every type in `BAR` is provided in `Foo` as specified -* every val binding in `BAR` is provided in `Foo` -* every exception in `BAR` is provided in `Foo` -* `Foo` can have more bindings than specified by `BAR` +The compiler checks that: +* Every type in `BAR` is provided in `Foo` as specified +* Every val binding in `BAR` is provided in `Foo` +* Every exception in `BAR` is provided in `Foo` + + + +### signature matching + +```sml +structure Foo :> BAR +``` + +`Foo` may have more bindings than specified by `BAR`, however they are not accessible from outside the module --- ### functors -a functor is a parameterized module +A functor is a parameterized module ```sml functor Functor (Module: SIG) = @@ -133,17 +157,24 @@ functor Functor (Module: SIG) = (*bindings*) end; ``` +> Somewhat like a template in C++ -applying a functor +### functors + +Applying a functor to a module creates a new module: ```sml structure FModule = Functor(Module); ``` +> Somewhat like a template instantiation in C++ + +### functors - exapmle + ```sml signature ORDERED_TYPE = sig type t @@ -159,6 +190,8 @@ end; +### functors - example + ```sml functor SortedList (Elt: ORDERED_TYPE) = struct fun add x [] = [x] @@ -173,6 +206,8 @@ end; +### functors - example + ```sml structure SortedIntList = SortedList(Int'); @@ -183,6 +218,8 @@ add 5 (add 6 (add 2 (add 4 (add 3 (add 1 []))))); +### functors - example + ```sml structure SortedStringList = SortedList(struct type t = string diff --git a/Material/Tutorials/sml/refs.md b/Material/Tutorials/sml/refs.md index d3038d8..81d8c2f 100644 --- a/Material/Tutorials/sml/refs.md +++ b/Material/Tutorials/sml/refs.md @@ -1,12 +1,33 @@ # Standard ML -## refs +## references --- +### pure functional programming + +Pure functional programming is achieved when: +* All data is immutable +* All functions are pure, which means: + 1. They have no side-effects + 1. They are completely deterministic + +Pure functional programming has its advantages, however it is not practical for most use cases. + + + +### pure functional programming + +So far we treated SML as a pure functional programming language. + +Now is a good time to tell you that this is not entirely true... + +--- + + ### ref cells -use `ref` to create a single **mutable** cell +In SML, we may use `ref` to create a **mutable** cell: ```sml val x = ref 4; @@ -15,7 +36,9 @@ val x = ref 4; -`ref` is a constructor and as such is also a function +### ref cells + +`ref` is a constructor and as such is also a function: ```sml ref; @@ -24,7 +47,9 @@ ref; --- -use `:=` to replace a cell's contents +### ref cells + +We may use `:=` to replace a cell's contents: ```sml val x = ref 0; @@ -33,7 +58,7 @@ x; ``` -note that `:=` returns `()` +> Note that `:=` returns `()` ```sml val := = fn : 'a ref * 'a -> unit @@ -41,7 +66,9 @@ val := = fn : 'a ref * 'a -> unit -use `!` to get the the cell's contents +### ref cells + +We may use `!` to get the cell's content: ```sml val x = ref 8; @@ -62,12 +89,18 @@ val ! = fn : 'a ref -> 'a ```sml fun swap x y = (x := !x + !y ; y := !x - !y ; x := !x - !y); + +val x = ref 1 and y = ref 2; +swap x y; +(!x, !y); ``` -an expression created by `;` evaluates to the value of the last expression +### sequencing with `;` + +An expression created by `;` evaluates to the value of the last expression ```sml val x = ref 42; @@ -77,22 +110,63 @@ val x = ref 42; --- -### memoization +### example - memoization + +Memoization is an optimization technique used to speed up computations by caching results. + +This is technique is applied to pure functions and relies on the fact that these are deterministic. + + + +### example - memoization + +For each function `f` that receives an argument `x` we store in memory pairs of `x` and `f(x)` values. + + + +### example - memoization + +When `f` is called with `x` we check if `f` was already called with `x` before. If so, we return the cached value, otherwise we compute `f(x)`, store it in memory and return it. + + + +### example - memoization + +First let's define a type for our cache memory (memoizer): ```sml type (''a, 'b) memoizer = {max_size: int, memory: (''a * 'b) list ref}; +``` + + + + -fun memoizer_put (memo: (''a, 'b) memoizer) x y = - #memory(memo) := - (if length (!(#memory memo)) < #max_size memo - then !(#memory memo) - else tl (!(#memory memo))) - @ [(x, y)]; +### example - memoization + +`memoizer_put` will let us store new values in the cache memory: + +```sml +fun memoizer_put (memo: (''a, 'b) memoizer) x y = + let + val state = #memory(memo) + in + state := + (if length (!state) < #max_size memo then + !(state) + else + tl (!state)) + @ [(x, y)] + end; ``` +### example - memoization + +`memoize` will be our memoization function, i.e. it will apply the memoization technique to a given function: + ```sml fun memoize (memo: (''a, 'b) memoizer) f x = case (List.find (fn t => x = #1 t) (!(#memory memo))) of @@ -100,7 +174,7 @@ fun memoize (memo: (''a, 'b) memoizer) f x = | NONE => ( let val y = f x in memoizer_put memo x y; - y + y end ); ``` @@ -108,6 +182,10 @@ fun memoize (memo: (''a, 'b) memoizer) f x = +### example - memoization + +Now let's apply memoization to the Fibonacci function: + ```sml local val memo = {max_size=10, memory=ref []} @@ -125,7 +203,9 @@ end; -let's compare +### example - memoization + +Let's compare: ```sml fib 43; diff --git a/Material/Tutorials/sml/sequences.md b/Material/Tutorials/sml/sequences.md index 93c47e7..3210164 100644 --- a/Material/Tutorials/sml/sequences.md +++ b/Material/Tutorials/sml/sequences.md @@ -6,30 +6,32 @@ ### lazy lists -* elements are not evaluated until their values are required -* **may** be infinite -* example: a sequence of all even integers `$0, 2, -2, 4, \ldots$` +* Elements are not evaluated until their values are required +* **May** be infinite +* Example: a sequence of all even integers `$0, 2, -2, 4, \ldots$` ### lazy lists in ML +ML evaluates `E` in `Cons(x,E)`, so to obtain laziness we must write `Cons(x, fn()=>E)` + ```sml datatype 'a seq = Nil | Cons of 'a * (unit -> 'a seq); -fun head (Cons (x, _)) = x; - -fun tail (Cons (_, xf)) = xf(); +fun take s 0 = [] + | take Nil _ = [] + | take (Cons (x, xf)) n = x :: take (xf()) (n-1); ``` -ML evaluates `E` in `Cons(x,E)`, so to obtain laziness we must write `Cons(x, fn()=>E)` - ---- + ### examples of sequences +`$1, 2, 3, 4, \ldots$` + ```sml fun from k = Cons (k, fn() => from (k+1)); @@ -38,38 +40,57 @@ from 1; ```sml -tail it; +take it 10; ``` +### examples of sequences + +`$0, 2, -2, 4, \ldots$` + ```sml -fun squares Nil = Nil - | squares (Cons (x, xf)) = - Cons (x*x, fn() => squares (xf())); +fun from_even n = let + val next = if n > 0 then ~n else ~n+2 + in + Cons (n, fn() => from_even next) + end; + +from_even 0; ``` + + +```sml +take it 10; +``` + +--- + +### Processing sequences + +`squares` takes a sequence of integers and returns a sequence of their squares: + ```sml +fun squares Nil = Nil + | squares (Cons (x, xf)) = + Cons (x*x, fn() => squares (xf())); + squares (from 1); ``` ```sml -head (tail (tail (tail (tail it)))); +take it 10; ``` ---- - -### elementary sequence processing - #### `addq` - -implement `addq` that takes two integer sequences and adds them element-wise +Implement `addq` that takes two integer sequences and adds them element-wise ```sml ... @@ -79,6 +100,7 @@ implement `addq` that takes two integer sequences and adds them element-wise +#### `addq` ```sml fun addq (Cons (x, xf), Cons (y, yf)) = Cons (x+y, fn() => addq (xf(), yf())) @@ -86,11 +108,17 @@ fun addq (Cons (x, xf), Cons (y, yf)) = ``` +```sml +addq (from 1, from 1); +take it 10; +``` + + #### `appendq` -implement `appendq` that appends two sequences +Implement `appendq` that appends two sequences ```sml ... @@ -100,6 +128,7 @@ implement `appendq` that appends two sequences +#### `appendq` ```sml fun appendq (Nil, yq) = yq | appendq (Cons(x, xf), yq) = @@ -107,13 +136,13 @@ fun appendq (Nil, yq) = yq ``` -what would `appendq(xq,yq)` be if `xq` is infinite? +What would `appendq(xq,yq)` be if `xq` is infinite? #### `mapq` -implement `mapq` that applies a function on the elements of a sequence +Implement `mapq` that applies a function on the elements of a sequence ```sml ... @@ -123,6 +152,7 @@ implement `mapq` that applies a function on the elements of a sequence +#### `mapq` ```sml fun mapq f Nil = Nil | mapq f (Cons (x,xf)) = @@ -130,11 +160,17 @@ fun mapq f Nil = Nil ``` +```sml +mapq (fn x => x*3) (from 1); +take it 10; +``` + + #### `filterq` -implement `filterq` that filters a sequence based on a predicate +Implement `filterq` that filters a sequence based on a predicate ```sml ... @@ -144,6 +180,7 @@ implement `filterq` that filters a sequence based on a predicate +#### `filterq` ```sml fun filterq pred Nil = Nil | filterq pred (Cons (x,xf)) = @@ -153,13 +190,19 @@ fun filterq pred Nil = Nil ``` +```sml +filterq (fn x => x mod 2 <> 0) (from 1); +take it 10; +``` + + #### `interleaveq` -implement `interleaveq` that interleaves two sequences +Implement `interleaveq` that interleaves two sequences. -e.g.: interleaving `1,2,3,...` and `11,12,13,...` returns: `1,11,2,12,3,13,4,...` +e.g.: Interleaving `1,2,3,...` and `11,12,13,...` returns: `1,11,2,12,3,13,4,...` ```sml ... @@ -169,6 +212,7 @@ e.g.: interleaving `1,2,3,...` and `11,12,13,...` returns: `1,11,2,12,3,13,4,... +#### `interleaveq` ```sml fun interleaveq (Nil, yq) = yq | interleaveq (Cons(x,xf),yq) = @@ -176,8 +220,15 @@ fun interleaveq (Nil, yq) = yq ``` +```sml +interleaveq (from 1, from 11); +take it 10; +``` + + +#### `dropq` `dropq` takes a sequence `s` and a positive number `n` and returns `s` without its first `n` elements ```sml @@ -188,6 +239,7 @@ fun interleaveq (Nil, yq) = yq +#### `dropq` ```sml fun dropq seq 0 = seq | dropq Nil _ = Nil @@ -195,8 +247,15 @@ fun dropq seq 0 = seq ``` +```sml +dropq (from 1) 10; +take it 10; +``` + + +#### `seqToList` `seqToList` takes a sequence and returns a list of its elements ```sml @@ -207,6 +266,7 @@ fun dropq seq 0 = seq +#### `seqToList` ```sml fun seqToList Nil = [] | seqToList (Cons(x, xf)) = x::(seqToList (xf())); @@ -214,7 +274,7 @@ fun seqToList Nil = [] - +#### `listToSeq` `listToSeq` takes a list and returns a sequence of its elements ```sml @@ -225,15 +285,17 @@ fun seqToList Nil = [] +#### `listToSeq` ```sml fun listToSeq [] = Nil | listToSeq (x::xs) = Cons (x, fn () => listToSeq xs); ``` - +--- -![q1](../imgs/q1.png) +### Exam Question +![q1](../../imgs/q1.png) @@ -243,6 +305,7 @@ fun listToSeq [] = Nil +#### `fraction` ```sml fun fraction m n = @@ -252,9 +315,17 @@ fun fraction m n = ``` + +```sml +fraction 1 7; +take it 10; +``` + + -![q2](../imgs/q2.png) +### Exam Question +![q2](../../imgs/q2.png) @@ -265,49 +336,52 @@ fun fraction m n = +#### `lazy_divide` + ```sml fun lazy_divide m n = (m div n, fraction (m mod n) n); ``` --- +## Sequences exercise +#### (a hard one in fact...) + + + +Let's define a lazy tree datatype, using sequences: ```sml datatype 'a seq = Nil | Cons of 'a * (unit -> 'a seq) + datatype 'a option = NONE | SOME of 'a; + datatype 'a node = Node of 'a * (unit -> 'a node option) * (unit -> 'a node option); -type 'a lazy_tree = unit -> 'a node option; -``` - - +type 'a lazy_tree = unit -> 'a node option; -```sml -fun take _ 0 = [] +fun take s 0 = [] | take Nil _ = [] - | take (Cons(x, xf)) n = x::(take (xf()) (n - 1)); + | take (Cons (x, xf)) n = x :: take (xf()) (n-1); -Control.Print.printLength := 1000; -Control.Print.printDepth := 1000; ``` -define some trees +Let's define some trees: ```sml fun t1 () = NONE; fun t2 0 () = SOME (Node (0, t1, t1)) | t2 n () = SOME (Node (n, t2 (n div 2), t2 (n - 1))); -fun t3 () = SOME (Node (100, t2 8, t2 7)); ``` -implement lazy bfs traversal of lazy trees +Implement lazy bfs traversal of lazy trees ```sml ... @@ -319,11 +393,17 @@ implement lazy bfs traversal of lazy trees ```sml local fun aux [] [] = Nil - | aux [] ts = aux (map (fn t => t ()) (List.rev ts)) [] + | aux [] ts = aux (map (fn t => t ()) ts) [] | aux (NONE::ns) ts = aux ns ts - | aux ((SOME (Node (h, l, r)))::ns) ts = Cons(h, fn () => aux ns (r::l::ts)) + | aux ((SOME (Node (h, l, r)))::ns) ts = + Cons(h, fn () => aux ns (ts @ [l, r])) in fun bfs t = aux [t ()] [] end; ``` + +```sml +take (bfs (t2 10)) 10; +``` + diff --git a/Material/Tutorials/theory/gradual_typing.md b/Material/Tutorials/theory/gradual_typing.md new file mode 100644 index 0000000..1740d86 --- /dev/null +++ b/Material/Tutorials/theory/gradual_typing.md @@ -0,0 +1,247 @@ +# Gradual Typing + +--- + +### Type Safety\Soundness + +The extent to which a programming language discourages or prevents type errors. + + + +### Static Typing + +Correctness of types is checked during compilation or before execution. + +> Examples: C++, Java, SML + + + +### Static Typing - Advantages + +* Type errors are caught early. +* Typed code is easier to optimize. + + + +### Static Typing - Disadvantages + +* Less flexible +* Requires a compiler\type checker. + + + +### Dynamic Typing + +Correctness of types is checked during execution. + +> Examples: JavaScript, Python + + + +### Dynamic Typing - Advantages + +* More flexible. +* In some sense easier to use. + + + +### Dynamic Typing - Disadvantages + +* Type errors are caught only during execution. +* Harder to scale and maintain. + +--- + +### Motivation + +Let's say we develop a simple social network, where users can post messages and comment on them. + +We want to deploy our website as quickly as possible, therefore we use Ruby (which is a dynamically typed, interpreted languages) to implement our backend. + + + +### Motivation + +As our user base grows, it requires a more complex backend. However, it is getting harder and harder to maintain and scale our code base. + +What should we do? Should we rewrite our backend in a statically typed language, let's say Scala? + +> Side note - Based on a true story. + + + +### Another Motivation + +Let's say we develop a more complex web application. We would like to use static typing in order to catch errors early and make our application more maintainable. + +However, browsers can only run JavaScript, and any library we would like to use is written in JavaScript as well. + + + +### Another Motivation + +Can we make our JavaScript code statically typed somehow? + +--- + +### Gradual Typing - Definition + +Correctness of types is checked either before or during execution. + + + +### Example - TypeScript + +TypeScript is a compiled, **gradually typed** language, which is a superset of JavaScript. + + + +Conside the following JavaScript code: + +```javascript +function add(a, b) { + return a + b; +} + +const result1 = add(5, 10); +const result2 = add("5", 10); + +console.log(result1, result2); +``` + +What will be printed to the console? + + + +Now let's "convert" it to TypeScript: + +```typescript +function add(a: number, b: number): number { + return a + b; +} + +const result1 = add(5, 10); +const result2 = add("5", 10); /* Compilation Error */ + +console.log(result1, result2); +``` + + + +### Gradual Typing with Typescript + +The `any` keyword can be used to disable type checking for a specific variable or function. + +For example the following code will pass compilation: + +```typescript +import * as lib from 'library_i_know_nothing_about' + +const value: any = lib.returnSomeValue(); + +console.log(value); +``` + + + +### Typescript `any` vs `unknown` + +While both `any` and `unknown` may be considered as "Top" types, there is a key difference - while `any` is not checked at all, `unknown` is checked at compile time: + + +```typescript +function foo(obj: any) { + return obj.field; /* No compilation error */ +} + +function bar(obj: unknown) { + return obj.field; /* Compilation error */ +} + +``` + + +### Typescript unsoundness + +Important Note! - TypeScript's type system is not sound, meaning that it is possible to write code that will pass compilation, but will cause type errors during execution. + +```typescript +function foo(obj: any) { + return obj.field; /* Will cause runtime type error! */ +} + +foo(2); +``` +--- + +### Example - C# + +C# is a compiled, statically typed programming language. + + + +C# version 4.0 introduced the `dynamic` type, which is a type that bypasses compile-time type checking. + +```csharp +static void Main(string[] args) +{ + dynamic MyVar = 100; + Console.WriteLine("Value: {0}", MyVar); + + MyDynamicVar = "Hello World!!"; + Console.WriteLine("Value: {0}", MyVar); +} +``` + + + +### Example - Dart + +Dart is a compiled, gradually typed language, that is compiled either to JavaScript or native code. + +Dart supports `dynamic` type as well: + +```dart +void main() +{ + dynamic a = 40; + print(a); + + a= "Dart"; + print(a); +} + +``` + + +### Examples comparison + +* TypeScript - Developed on top of a dynamically typed language in order to improve soundness. +* C# - Was originally a strictly statically typed language, but added a dynamic type in order to improve flexibility. +* Dart - Supports gradual typing from day 1. +--- + +## Gradual Typing Excercise + + + +The TAs of PL class has argued whether SML is a gradually typed language or not. + +Adi, the head TA, claims that SML is a gradually typed language, since not all values must be annotated with types. + +Is Adi correct? + + + +### Answer + +Adi is not correct. + +SML is strictly statically typed. even though not all values must be annotated, their type must be inferred at **compile time**. Each value has a single concrete type that is known and being checked at compile time. + +--- + +### Drawbacks of Gradual Typing + +* Types might not be reliable or checked +* Ensuring type safety is expensive diff --git a/Material/Tutorials/theory/rtti.md b/Material/Tutorials/theory/rtti.md index dabb3f5..23e9ce0 100644 --- a/Material/Tutorials/theory/rtti.md +++ b/Material/Tutorials/theory/rtti.md @@ -2,21 +2,28 @@ --- +### motivation + +Let's say we would like to implement a garbage collection algorithm, e.g. mark & sweep or stop & copy. + ### motivation -we would like to implement a garbage collection algorithm, i.e., mark & sweep or stop & copy. +In order to implement the algorithm, we need a way to find all "reachable" variables from a certain pile of bits. -either way, we need a way to find all "reachable" variables from a certain pile of bits. how do we traverse a network of -data connected to other data? +How do we traverse a network of +data connected to other data? -for example, a class instance may have fields in it, which may themselves have fields in them. -the same 16 bytes may be interpreted in countless different ways, and when we reach a value, we do not know what's in it! + +### motivation + +For example, a class instance may have fields in it, which may themselves have fields in them. +the same 16 bytes may be interpreted in countless different ways, and when we reach a value, we may not know what's in it! -#### example +### example consider the following C `struct`s ```C struct S1 { @@ -28,35 +35,49 @@ struct S2 { ``` -assuming `sizeof(int) == sizeof(int*) && sizeof(int*) == 8`, and given an 8 bit long variable, how can we know if it has to be interpreted as `S1` or as `S2`? -this is a problem because it shows that *we cannot tell apart pointers and data*, making implementing GC impossible. +Assume `sizeof(int) == sizeof(int*) && sizeof(int*) == 8`. + +Given an 8 byte long variable, how can we know if it has to be interpreted as `S1` or as `S2`? + + + +This is a problem because it shows *we cannot tell apart pointers and data*, making implementing GC impossible. + +Conclusion: we must have type information at *runtime* + + + +The problem of traversal on networks of objects is not specific to garbage collection algorithms, and recurs in various other places like deep cloning and serialization. -conclusion: we must have type information at *runtime* -the problem of traversal on networks of objects is not specific to garbage collection algorithms, and recurs in various other places like deep cloning and serialization. -it is especially pronounced in languages with dynamic typing - types must be checked in runtime, and therefore, we must save type information so it is available in runtime. +It is especially pronounced in languages with dynamic typing - types must be checked in runtime, and therefore, we must save type information so it is available in runtime. + -statically-typed languages can check for *type errors* in compile time and from there assume all field accesses are valid. +Statically-typed languages can check for *type errors* in compile time and from there assume all field accesses are valid. -in dynamically-typed languages RTTI is necessary for type checking +In dynamically-typed languages, RTTI is necessary for type checking statically-typed languages can benefit from RTTI too --- ### the solution + -**RTTI**: runtime type information -- additional data attached to each cell in memory -- specifies the type of the data to which it is attached -- is useful in both statically- and dynamically-typed languages + +**RTTI**: Runtime Type Information +- Additional data attached to each cell in memory +- Specifies the type of the data to which it is attached +- Is useful in both statically and dynamically typed languages + + RTTI is used for: -* serialization -* deep-cloning -* garbage collection +* Serialization +* Deep-cloning +* Garbage collection NOTE: these are purposes that require traversal of a type structure that has to be known in runtime @@ -64,24 +85,29 @@ in dynamically-typed languages, also: * type checking -C & C++ have a "no hidden costs" policy - when a programmer allocates memory, it should be guaranteed that no extra memory is used. + +C has a "no hidden costs" policy (also known as *the supermarket principle*) - when a programmer allocates memory, it should be guaranteed that no extra memory is used. therefore, these languages *do not* have RTTI -NOTE: except for a very limited form in C++ for the implementation of `virtual`, i.e., `vptr` and `vtbl` +> Note that C++ does hold RTTI in some objects, more on that in OOP class + + -this is why these languages **do not, and can not**, have general purpose GC, serialization, cloning or any "deep" operations. +This is why these languages **do not, and can not**, have general purpose GC, serialization, cloning or any "deep" operations. -let's consider the following snippet regarding a variable `today` which has fields `day`, `month`, `year`: + +Let's consider the following snippet regarding a variable `today` which has fields `day`, `month`, `year`: ```javascript today.day = 35; ``` -this snippet is valid Java as well as valid JavaScript. however, when ran, both languages work differently to interpret it. +This snippet is valid Java as well as valid JavaScript. however, when ran, both languages work differently to interpret it. -in Java, we first dereference `today`, ignoring RTTI (it is not used for field access in Java), advance by `off(day)` and update the field. -the offset `off(day)` is known in compile-time, because Java is statically-typed. +In Java, we first dereference `today`, ignoring RTTI (it is not used for field access in Java), advance by `off(day)` and update the field. -in JavaScript, we also dereference `today`, but we must examine the RTTI to determine `off(day)` to then advance by it and update the field. +The offset `off(day)` is known in compile-time, because Java is statically-typed. + +In JavaScript, we also dereference `today`, but we must examine the RTTI to determine `off(day)` to then advance by it and update the field. this is because JavaScript is dynamically-typed. NOTE: in this case, C would operate similarly to Java, except it has no RTTI to ignore. @@ -89,4 +115,4 @@ NOTE: in this case, C would operate similarly to Java, except it has no RTTI to JavaScript's dynamic typing causes it to not know the required offsets in compile time. -this example demonstrates the difference between statically- and dynamically-typed languages in their use of RTTI. +This example demonstrates the difference between statically- and dynamically-typed languages in their use of RTTI. diff --git a/Material/Tutorials/theory/sml-type-system.md b/Material/Tutorials/theory/sml-type-system.md deleted file mode 100644 index 4aa8b66..0000000 --- a/Material/Tutorials/theory/sml-type-system.md +++ /dev/null @@ -1,217 +0,0 @@ -# types - -## type constructors - ---- - -### Types - -> A type is defined as a set of values. - -We will want to expand our possibilities by creating new types with existing ones. Which is why we will learn on Type Constructors. - ---- - -### what are type constructors? - -> type constructors are operators that take a type and return a new type - ---- - -### power set - -```pascal -type Alphabets = set of 'A' .. 'Z'; -``` - ---- - -### product - -* notation: $T_1\times \cdots \times T_n$ -* composition: $\langle\cdot,\ldots,\cdot\rangle$ -* decomposition: $i(\cdot)$ - - - -in SML: - -```sml -type product = int * real * string; -``` - - - - -#### properties of cartesian products - -* commutativity - never! -* associativity - depending on the PL semantics. -* for example $ R\times S \times T$ and $R\times (S \times T)$ and $(R\times S) \times T$ - * structural ✅ - * nominal ❌ - ---- - -### integral exponentiation - -* integral exponentiation makes homogenous tuples -* notation: $T^n = T \times \overset{\text{n times}}{\cdots} \times T$ - ---- - -### unit type - -a type with only one value - -(similar to `void` but not really) - - - -in SML: - -* is either - * empty record `{}` - * empty tuple `()` -* Predefined name: `Unit` -* Only value: `()` - ---- - -### branding - -* notation: $l(T)$ -* composition $l(\cdot)$ -* decomposition: $l(\cdot)$ - - - -in SML: - -```sml -datatype X = X of int; -``` - - - - -note that `type` creates an alias, it doesn't brand - -```sml -type X = int; -fun (x: X): int = x; (*OK*) -``` - - ---- - -### records - -* notation: $\{ l_1: T_1,\ldots,l_n: T_n \}$ -* composition: $\{ l_1=\cdot,\ldots,l_n=\cdot \}$ -* decomposition: $l_i(\cdot)$ - - - -in SML: - -```sml -type record = {x: int, y: real}; -``` - - ---- - -### disjoint union - -* notation: $l_1(T_1)\cup\cdots\cup l_n(T_n)$ -* composition: $l_i(\cdot)$ - - - -in SML: - -```sml -datatype ('a, 'b) union = A of 'a | B of b; -``` - - - - -an enum can be thought of as a disjoint union of branded unit types - -$$(l_1, \ldots, l_n) = l_1(Unit) + \ldots + l_n(Unit)$$ - ---- - -### The empty/null/bottom type - -usually by disjoint sum of a list of zero types - - - -#### Not in SML! - -* a datatype can never be empty -* best approximation for a function that never returns: - * Return type is `Unit` - * Function always throws an exception - ---- - -### the Any/all/top type - -a type that contains **all** values in the language - ---- - -### mapping and partial mapping - -* notation: $S\rightarrow T$ - - - -in SML: - -```sml -type ('a, 'b) F = 'a -> 'b; -``` - - ---- - -### simple recursive - -* notation: $\tau = E(\tau,T_1,\ldots,T_n)$ - - - -in SML: - -```sml -datatype 'a list = - nil - | :: of 'a * 'a list; -``` - - ---- - -### multiple recursive - -* notation: - - $$\tau_1 = E(T_1,\ldots,T_m, \tau_1,\ldots,\tau_n)$$ - $$\tau_n = E(T_1,\ldots,T_m, \tau_1,\ldots,\tau_n)$$ - - - -in SML: - -```sml -datatype 'a foo = Foo of ('a * 'a bar) -and 'a bar = Bar of 'a foo | None; - -Foo (1, Bar (Foo (2, None))); -``` - diff --git a/Material/Tutorials/theory/type_constructors.md b/Material/Tutorials/theory/type_constructors.md new file mode 100644 index 0000000..d4be7a8 --- /dev/null +++ b/Material/Tutorials/theory/type_constructors.md @@ -0,0 +1,330 @@ + +# type constructors + +--- + +### Types + +> A type is defined as a set of values. + +Most programming languages have at least some basic built-in types. + +However, usually we would like to use some more complex types or even define new types ourselves. + + + +### type constructors + +Type constructors are operators that take a type and return a new type. + +--- + +## Type Constructor Examples + +--- + +### power set + +> $\wp T = \\{ T' | T' \subseteq T \\}$ + +Which languages support the power set type constructor? + + + +### power set + +In Pascal: + +```pascal +type Alphabets = set of 'A' .. 'Z'; +``` + +In Python: + +```python +s: set[int] = {1, 2, 3} +``` + +--- + +### cartesian product + +* Notation: $T_1\times \cdots \times T_n$ +* Composition: $\langle\cdot,\ldots,\cdot\rangle$ +* Decomposition: $i(\cdot)$ or $(\cdot)\\#i$ + +How is cartesian product represented in languages you know? + + + +### cartesian product + +In SML: + +```sml +type product = int * real * string; +``` + +In TypeScript: +```typescript +type product = [number, number, string]; +``` + + + +### properties of cartesian products + +* Commutativity - never! +* Associativity - depending on the PL semantics. + +--- + +### integral exponentiation + +* Integral exponentiation makes homogenous tuples +* Notation: $T^n = T \times \overset{\text{n times}}{\cdots} \times T$ + + + +### integral exponentiation(ish) + +In C/C++: + +```c +typedef int exp[10]; +``` + +> C compiler will actually treat this as a `int*` + +--- + +### unit type + +A type with only one value + +(similar to `void` but not really) + + + +### unit type + +In SML, either `{}` or `()`: + +```sml +(); +(*val it = () : unit*) + +``` + +```sml +() = {}; +(*val it = true : bool*) + +``` + +--- + +### branding + +* Notation: $l(T)$ +* Composition $l(\cdot)$ +* Decomposition: $l(\cdot)$ + +> Branding "requirement" - $\forall v\in T: v \neq l(v)$ + + + +### branding + +In SML: + +```sml +datatype X = X of int; +``` + + + + +### branding + +Note that `type` creates an alias, it doesn't brand + +```sml +type X = int; +fun (x: X): int = x; (*OK*) +``` + + +--- + +### records + +* Notation: $\{ l_1: T_1,\ldots,l_n: T_n \}$ +* Composition: $\{ l_1=\cdot,\ldots,l_n=\cdot \}$ +* Decomposition: $l_i(\cdot)$ + + + +### records + +In SML: + +```sml +type record = { x: int, y: real }; +``` + +In C: +```c +struct record = { int x; int y; }; +``` + +In TypeScript: +```typescript +type record = { x: number, y: number }; +``` + +--- + +### disjoint union + +* Notation: $l_1(T_1)\cup\cdots\cup l_n(T_n)$ +* Composition: $l_i(\cdot)$ + + + +### disjoint union + +In SML: + +```sml +datatype ('a, 'b) union = A of 'a | B of 'b; +``` + +In TypeScript (not actually disjoint): +```typescript +type union = string | number; +``` + + + +A (true) enum can be thought of as a disjoint union of branded unit types + +$$(l_1, \ldots, l_n) = l_1(Unit) + \ldots + l_n(Unit)$$ + +> Do C enums follow this definition? How about Pascal enums? + +--- + +### The bottom type + +A type that has **no** values + +> Formally - $\emptyset$ + + + +### The bottom type + +In TypeScript, using the `never` keyword: +```typescript +let x: never; +x = 2 /* Type error, no matter what is on the right side of the assignment */ +``` + +In C - we will use *void* + + + +### The bottom type - Not in SML! + +A datatype in SML can never be empty. + +Best approximation for a function that never returns: +* Return type is `Unit` +* Function always throws an exception + +--- + +### the Any/all/top type + +A type that contains **all** values in the language. + + + +### the Any/all/top type + +In TypeScript, using the `unknown` keyword: +```typescript +let x: unknown; +x = 2; +x = 'Hello'; +x = [ true, false ]; +``` + +--- + +### mapping and partial mapping + +Notation: $S\rightarrow T$ + + + +### mapping and partial mapping + +Practically - a function type! + +In SML: + +```sml +type ('a, 'b) F = 'a -> 'b; +``` + +In TypeScript: +```typescript +type pred = (unknown) => boolean; +``` + + +--- + +### simple recursive + +Notation: $\tau = E(\tau,T_1,\ldots,T_n)$ + + + +### simple recursive + +In SML: + +```sml +datatype 'a list = + nil + | :: of 'a * 'a list; +``` + + +--- + +### multiple recursive + +Notation: + +$$\tau_1 = E(T_1,\ldots,T_m, \tau_1,\ldots,\tau_n)$$ +... +$$\tau_n = E(T_1,\ldots,T_m, \tau_1,\ldots,\tau_n)$$ + + + +### multiple recursive +In SML: + +```sml +datatype 'a foo = Foo of ('a * 'a bar) +and 'a bar = Bar of 'a foo | None; + +Foo (1, Bar (Foo (2, None))); +``` + +Have we seen another datatype in SML defined by multiple recursion? +