From 441c5e9b8bcb42f80a8840a2def6b4e856de7780 Mon Sep 17 00:00:00 2001 From: MatthijsBlom <19817960+MatthijsBlom@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:53:13 +0200 Subject: [PATCH 1/7] update build script --- site.hs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/site.hs b/site.hs index 4b1d9140..1cafd16a 100644 --- a/site.hs +++ b/site.hs @@ -28,7 +28,12 @@ import Text.Pandoc.Walk as Pandoc (query) import Text.Pandoc.Shared as Pandoc (stringify) import Text.Pandoc.Class (runIO) import Text.Pandoc.Readers.Markdown (readMarkdown) -import Text.Pandoc.Options (readerExtensions, Extension(Ext_implicit_figures), ReaderOptions) +import Text.Pandoc.Options + ( Extension (Ext_implicit_figures), + HTMLMathMethod (MathML), + ReaderOptions (readerExtensions), + WriterOptions (writerHTMLMathMethod), + ) import Text.Pandoc.Extensions (disableExtension) import System.Directory (listDirectory) import Control.Monad (forM_) @@ -187,7 +192,7 @@ chapterCtx mprev mnext = -- Custom pandoc compiler that uses our custom reader options customPandocCompiler :: Compiler (Item String) -customPandocCompiler = pandocCompilerWith customReaderOptions defaultHakyllWriterOptions +customPandocCompiler = pandocCompilerWith customReaderOptions customWriterOptions -- Custom reader options that disable implicit_figures extension customReaderOptions :: ReaderOptions @@ -196,6 +201,12 @@ customReaderOptions = defaultHakyllReaderOptions (readerExtensions defaultHakyllReaderOptions) } +customWriterOptions :: WriterOptions +customWriterOptions = + defaultHakyllWriterOptions + { writerHTMLMathMethod = MathML + } + -- Helper function to pair each element with its previous and next elements zipPrevNext :: [a] -> [(Maybe a, a, Maybe a)] zipPrevNext xs = zip3 (Nothing : map Just xs) xs (map Just (drop 1 xs) ++ [Nothing]) From a0ea2db5e62517a9d4f4290f85b7d893ec545925 Mon Sep 17 00:00:00 2001 From: MatthijsBlom <19817960+MatthijsBlom@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:53:59 +0200 Subject: [PATCH 2/7] set comprehension --- source_md/starting-out.md | 4 ++-- static/assets/images/starting-out/setnotation.png | Bin 848 -> 0 bytes 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 static/assets/images/starting-out/setnotation.png diff --git a/source_md/starting-out.md b/source_md/starting-out.md index 8f261fd1..89e2f0b7 100644 --- a/source_md/starting-out.md +++ b/source_md/starting-out.md @@ -626,8 +626,8 @@ Although it's simpler to just use the `replicate`{.label .function} function if ![frog](assets/images/starting-out/kermit.png){.left width=180 height=156} If you've ever taken a course in mathematics, you've probably run into *set comprehensions*. They're normally used for building more specific sets out of general sets. -A basic comprehension for a set that contains the first ten even natural numbers is ![set notation](assets/images/starting-out/setnotation.png). -The part before the pipe is called the output function, `x` is the variable, `N` is the input set and `x <= 10` is the predicate. +A basic comprehension for a set that contains the first ten even natural numbers is $S = \left\{ 2 \cdot x \mid x \in \mathbb N, x \le 10 \right\}$. +The part before the pipe is called the output function, $x$ is the variable, $\mathbb N$ is the input set and $x \le 10$ is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate. If we wanted to write that in Haskell, we could do something like `take 10 [2,4..]`. diff --git a/static/assets/images/starting-out/setnotation.png b/static/assets/images/starting-out/setnotation.png deleted file mode 100644 index ff1667107c9b9b3f9387b7a901c0e4962b683bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 848 zcmV-W1F!svP)-NKtMnM002->P`0+Vo}QkHii!*j35I>L~vJl>zS4xLDQ$G9& zGfH6k4;u$5wRrJx%WBR`A|QP67&n3Itz^ul3nr!eJ{r>QkAzUg?)8pq9M&|0t+V4w zr@%1#VcfVG6L^lf5|ha*zVBh%C)!B0oO=$8HeB5I{j_~{Mm&d4OYw1Bz~E96IG?#~ z0XiRpj&@v?w|nIyuFSlW{TO#~5bb<0Xcmx7;x3@DcM7dIb6Vrd<(1zH z*KUFPDXs^qr4KNkWkWN#{0@8KZd4BLxVXxd%na^!Y$P>b(Oggv-1TsqHMDn~-_EjH z`nZjQfWnHA#`q1F);n&LvQ^463W4CVGJ#OzaoJ?f$0smWu3WgCwdPPo+`usY^>7;p zsDO_pwZY9F!y(#_!eiDNZ*6Z#pTtT;-N|md)=Fc-)h;%o9&Y2nL%&o8xH_p8v~8wq z+S%H-IuDhvnxxd@+O5_!^Sfb*n+`I!gW9c~M;A=L#*Ka5X$X6g#FOxFTWUhl(EM7Q zawEr5Rj+C5cH5VbkOz?tv#+(V Date: Wed, 8 Apr 2026 01:54:23 +0200 Subject: [PATCH 3/7] function composition --- source_md/higher-order-functions.md | 2 +- .../images/higher-order-functions/composition.png | Bin 702 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 static/assets/images/higher-order-functions/composition.png diff --git a/source_md/higher-order-functions.md b/source_md/higher-order-functions.md index a5da42b8..c7447f0b 100644 --- a/source_md/higher-order-functions.md +++ b/source_md/higher-order-functions.md @@ -865,7 +865,7 @@ ghci> map ($ 3) [(4+), (10*), (^2), sqrt] ## Function composition {#composition} -In mathematics, function composition is defined like this: ![ (f . g)(x) = f(g(x))](assets/images/higher-order-functions/composition.png), meaning that composing two functions produces a new function that, when called with a parameter, say, *x* is the equivalent of calling *g* with the parameter *x* and then calling the *f* with that result. +In mathematics, function composition is defined like this: $\left( f \circ g \right) (x) = f \left( g(x) \right)$, meaning that composing two functions produces a new function that, when called with an argument, say, $x$ is the equivalent of calling $g$ with the argument $x$ and then calling the $f$ with that result. In Haskell, function composition is pretty much the same thing. We do function composition with the `.` function, which is defined like so: diff --git a/static/assets/images/higher-order-functions/composition.png b/static/assets/images/higher-order-functions/composition.png deleted file mode 100644 index b422bb155d9be11ca58fd23ca6ee62d4557ecb0c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 702 zcmV;v0zv(WP)Q)>l2mR_Y1qTO1;v~aj{nb-G-sQr((6gMDKHSX?Ieo z(m5{n8en!9)^#du_$ARy0}gg4l`5U%Vy^<4W_agOYGH9)?ttAI8c%$CxcH0uENy}`~kYXj>kajF1wv)?JPo3tKFiX-EOF`&o)%!q z-w?h_-!XVQvJQCi)UJ1)W>aS|96SZetfOa7J4d%4$U5N3(^tBSes}?VQs?4nd|#s? zgCC`*ouk_iWF0VASy9c%XZKK1(TWs1R21Mmkoc*hCda;*$SR-l96RqsRsn62onK3`7R_Ri6a$>{P1`355VYsU{DPp Date: Wed, 8 Apr 2026 01:55:37 +0200 Subject: [PATCH 4/7] various --- source_md/higher-order-functions.md | 14 +++++++------- source_md/starting-out.md | 2 +- source_md/syntax-in-functions.md | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/source_md/higher-order-functions.md b/source_md/higher-order-functions.md index c7447f0b..ff7ccc77 100644 --- a/source_md/higher-order-functions.md +++ b/source_md/higher-order-functions.md @@ -796,14 +796,14 @@ ghci> scanl (flip (:)) [] [3,2,1] When using a `scanl`, the final result will be in the last element of the resulting list while a `scanr` will place the result in the head. Scans are used to monitor the progression of a function that can be implemented as a fold. -Let's answer us this question: **How many elements does it take for the sum of the square roots of all natural numbers to exceed 1000?** +Let's answer us this question: **How many elements does it take for the sum of the square roots of all natural numbers to exceed $1000$?** To get the square roots of all natural numbers, we just do `map sqrt [1..]`. Now, to get the sum, we could do a fold, but because we're interested in how the sum progresses, we're going to do a scan. -Once we've done the scan, we just see how many sums are under 1000. -The first sum in the scanlist will be 1, normally. -The second will be 1 plus the square root of 2. -The third will be that plus the square root of 3. -If there are X sums under 1000, then it takes X+1 elements for the sum to exceed 1000. +Once we've done the scan, we just see how many sums are under $1000$. +The first sum in the scanlist will be $1$, normally. +The second will be $1 + \sqrt 2$. +The third will be $1 + \sqrt 2 + \sqrt 3$. +If there are $n$ sums under 1000, then it takes $n + 1$ elements for the sum to exceed $1000$. ```{.haskell:hs} sqrtSums :: Int @@ -820,7 +820,7 @@ ghci> sum (map sqrt [1..130]) ``` We use `takeWhile` here instead of `filter` because `filter` doesn't work on infinite lists. -Even though we know the list is ascending, `filter` doesn't, so we use `takeWhile` to cut the scanlist off at the first occurrence of a sum greater than 1000. +Even though we know the list is ascending, `filter` doesn't, so we use `takeWhile` to cut the scanlist off at the first occurrence of a sum greater than $1000$. ## Function application with $ {#function-application} diff --git a/source_md/starting-out.md b/source_md/starting-out.md index 89e2f0b7..e6616735 100644 --- a/source_md/starting-out.md +++ b/source_md/starting-out.md @@ -884,7 +884,7 @@ ghci> triangles = [ (a,b,c) | c <- [1..10], a <- [1..10], b <- [1..10] ] We're just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out `triangles` in GHCi, you'll get a list of all possible triangles with sides under or equal to 10. Next, we'll add a condition that they all have to be right triangles. -We'll also modify this function by taking into consideration that side *a* isn't larger than the hypotenuse and that side *b* isn't larger than side *a*. +We'll also modify this function by taking into consideration that side $a$ isn't larger than the hypotenuse and that side $b$ isn't larger than side $a$. ```{.haskell: .ghci} ghci> rightTriangles = [ (a,b,c) | c <- [1..10], a <- [1..c], b <- [1..a], a^2 + b^2 == c^2] diff --git a/source_md/syntax-in-functions.md b/source_md/syntax-in-functions.md index 29501e61..11457eba 100644 --- a/source_md/syntax-in-functions.md +++ b/source_md/syntax-in-functions.md @@ -91,7 +91,7 @@ When making patterns, we should always include a catch-all pattern so that our p Pattern matching can also be used on tuples. What if we wanted to make a function that takes two vectors in a 2D space (that are in the form of pairs) and adds them together? -To add together two vectors, we add their x components separately and then their y components separately. +To add together two vectors, we add their $x$ components separately and then their $y$ components separately. Here's how we would have done it if we didn't know about pattern matching: ```{.haskell:hs} From 2a42d7366ecdf8f302df05474a938bf6c6e72560 Mon Sep 17 00:00:00 2001 From: MatthijsBlom <19817960+MatthijsBlom@users.noreply.github.com> Date: Wed, 8 Apr 2026 01:55:51 +0200 Subject: [PATCH 5/7] Fibonacci --- source_md/recursion.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/source_md/recursion.md b/source_md/recursion.md index f738f8b6..516f705f 100644 --- a/source_md/recursion.md +++ b/source_md/recursion.md @@ -17,14 +17,14 @@ Recursion is actually a way of defining functions in which the function is appli Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. -We say that *F(0) = 0* and *F(1) = 1*, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. +We say that $F(0) = 0$ and $F(1) = 1$, meaning that the 0th and 1st fibonacci numbers are $0$ and $1$, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. -So *F(n) = F(n-1) + F(n-2)*. -That way, *F(3)* is *F(2) + F(1)*, which is *(F(1) + F(0)) + F(1)*. -Because we've now come down to only non-recursively defined fibonacci numbers, we can safely say that *F(3)* is 2. -Having an element or two in a recursion definition defined non-recursively (like *F(0)* and *F(1)* here) is also called the **edge condition** and is important if you want your recursive function to terminate. -If we hadn't defined *F(0)* and *F(1)* non recursively, you'd never get a solution any number because you'd reach 0 and then you'd go into negative numbers. -All of a sudden, you'd be saying that *F(-2000)* is *F(-2001) + F(-2002)* and there still wouldn't be an end in sight! +So $F(n) = F(n-1) + F(n-2)$. +That way, $F(3)$ is $F(2) + F(1)$, which is $(F(1) + F(0)) + F(1)$. +Because we've now come down to only non-recursively defined fibonacci numbers, we can safely say that $F(3)$ is $2$. +Having an element or two in a recursion definition defined non-recursively (like $F(0)$ and $F(1)$ here) is also called the **edge condition** and is important if you want your recursive function to terminate. +If we hadn't defined $F(0)$ and $F(1)$ non recursively, you'd never get a solution any number because you'd reach $0$ and then you'd go into negative numbers. +All of a sudden, you'd be saying that $F(-2000)$ is $F(-2001) + F(-2002)$ and there still wouldn't be an end in sight! Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something *is* instead of declaring *how* you get it. That's why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is. From a60bab28a40795d22895ddd642a0111bfe343412 Mon Sep 17 00:00:00 2001 From: MatthijsBlom <19817960+MatthijsBlom@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:53:56 +0200 Subject: [PATCH 6/7] polynomials --- source_md/modules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_md/modules.md b/source_md/modules.md index 2c0d4199..e0d07f5e 100644 --- a/source_md/modules.md +++ b/source_md/modules.md @@ -132,7 +132,7 @@ ghci> transpose ["hey","there","folks"] ["htf","eho","yel","rk","es"] ``` -Say we have the polynomials *3x^2^ + 5x + 9*, *10x^3^ + 9* and *8x^3^ + 5x^2^ + x - 1* and we want to add them together. +Say we have the polynomials $3x^2 + 5x + 9$, $10x^3 + 9$ and $8x^3 + 5x^2 + x - 1$ and we want to add them together. We can use the lists `[0,3,5,9]`, `[10,0,0,9]` and `[8,5,1,-1]` to represent them in Haskell. Now, to add them, all we have to do is this: From 65c395bc3c6c33b80d8dacfa52d4ad3041654d30 Mon Sep 17 00:00:00 2001 From: MatthijsBlom <19817960+MatthijsBlom@users.noreply.github.com> Date: Tue, 21 Apr 2026 18:47:06 +0200 Subject: [PATCH 7/7] use pandoc superscript syntax --- source_md/recursion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source_md/recursion.md b/source_md/recursion.md index 516f705f..9fb54bec 100644 --- a/source_md/recursion.md +++ b/source_md/recursion.md @@ -17,7 +17,7 @@ Recursion is actually a way of defining functions in which the function is appli Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. -We say that $F(0) = 0$ and $F(1) = 1$, meaning that the 0th and 1st fibonacci numbers are $0$ and $1$, respectively. +We say that $F(0) = 0$ and $F(1) = 1$, meaning that the 0^th^ and 1^st^ fibonacci numbers are $0$ and $1$, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So $F(n) = F(n-1) + F(n-2)$. That way, $F(3)$ is $F(2) + F(1)$, which is $(F(1) + F(0)) + F(1)$.