From 38761a82b8202e0084cad13614f62fea3e0af051 Mon Sep 17 00:00:00 2001
From: johnW_ret <19534013+johnW-ret@users.noreply.github.com>
Date: Wed, 17 Apr 2024 13:20:33 -0500
Subject: [PATCH] Updated Partial Application section
Rewrote last example
---
index.ipynb | 223 +++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 212 insertions(+), 11 deletions(-)
diff --git a/index.ipynb b/index.ipynb
index 4ab61f7..6d8dd67 100644
--- a/index.ipynb
+++ b/index.ipynb
@@ -1586,12 +1586,124 @@
"id": "215f5f1e",
"metadata": {},
"source": [
- "This feature is called **partial application**. On its surface, it's nice for adding convenient names to helper functions. In practice, it can allow you to express complexity using simple, modular pieces:"
+ "This feature is called **partial application**. It can help you express complexity using simple, modular pieces:"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "80ae388c",
+ "metadata": {},
+ "source": [
+ "Here we build `add` and `divide` from `combine` by passing `+` and `/` to it. This code doesn't do much anything useful though... "
]
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 174,
+ "metadata": {
+ "dotnet_interactive": {
+ "language": "fsharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "fsharp"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "(7, 2)
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "// y applied to x applied to f\n",
+ "let combine f x y = f x y\n",
+ "let add = combine (+) // a way to pass the + function\n",
+ "let divide = combine (/)\n",
+ "\n",
+ "add 3 4, divide 9 4"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2bbb29e9",
+ "metadata": {},
+ "source": [
+ "We can pass a `check` function to `combine` that can perform a check and decide whether we want to continue the computation or not."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "92fbc8a2",
+ "metadata": {},
+ "source": [
+ "`f` comes first in the parameter list because we probably want will want to bind it first then later decide what type of check behavior we want."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "ae36a578",
+ "metadata": {
+ "dotnet_interactive": {
+ "language": "fsharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "fsharp"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "let combine f check x y =\n",
+ " check f x y"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "49329736",
+ "metadata": {},
+ "source": [
+ "We can build all different kinds of \"adders\" from `combine`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 213,
"id": "265d4712",
"metadata": {
"dotnet_interactive": {
@@ -1606,26 +1718,115 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "8\n",
- "NaN\n"
+ "9\n",
+ "Adding 5 + 6...\n",
+ "11\n",
+ "8\n"
]
}
],
"source": [
- "let combine f check x y =\n",
- " f <|| check x y\n",
+ "let normalize f x y = f (abs x) (abs y)\n",
"\n",
- "let id2 x y = x, y\n",
+ "let normalizeThenAdd = combine (+) normalize\n",
+ "normalizeThenAdd -4 5 |> printfn \"%d\"\n",
"\n",
- "let add = combine (+) id2\n",
- "add 3 5 |> printfn \"%d\"\n",
+ "let printThenAdd = combine (+) (fun f x y -> printfn \"Adding %d + %d...\" x y; f x y)\n",
+ "printThenAdd 5 6 |> printfn \"%d\"\n",
"\n",
- "let divideThen check = combine (/) check\n",
- "let safeDivide = divideThen (fun x y -> if y = 0 then x, nan else x, y)\n",
+ "let add = combine (+) id // id is a special function that means \"do nothing\" in this context\n",
+ "add 3 5 |> printfn \"%d\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1c89cc9d",
+ "metadata": {},
+ "source": [
+ "We can build \"safe dividers\" that checks whether the denominator = 0."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1ca4528b",
+ "metadata": {},
+ "source": [
+ "`safeDivide` replaces `y` with `NaN` when `y = 0`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 206,
+ "metadata": {
+ "dotnet_interactive": {
+ "language": "fsharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "fsharp"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "NaN\n"
+ ]
+ }
+ ],
+ "source": [
+ "let ``convert divBy0 to NaN`` f x y =\n",
+ " f x (if y = 0 then nan else y)\n",
"\n",
+ "let safeDivide = combine (/) ``convert divBy0 to NaN``\n",
"safeDivide 4 0 |> printfn \"%f\""
]
},
+ {
+ "cell_type": "markdown",
+ "id": "12c206eb",
+ "metadata": {},
+ "source": [
+ "`safeDivide` implicitly evaluates to a `float`, though, because `NaN` is not a valid value for `int`s. Sometimes you absolutely do want integer division, which evaluates to an `int` and ignores the remainder.\n",
+ "\n",
+ "`tryDivide` checks if `y = 0`, and if it is, it avoids doing the division altogether (by not evaluating `cont`).\n",
+ "> ℹ️ Note\n",
+ "> \n",
+ "> I should move this example down further to when I explain Option types, perhaps referencing this example. It is not 100% clear what is going on here without explaining option types."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 219,
+ "metadata": {
+ "dotnet_interactive": {
+ "language": "fsharp"
+ },
+ "polyglot_notebook": {
+ "kernelName": "fsharp"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Some(4)\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "let ``convert divBy0 to None`` cont x y =\n",
+ " if y = 0 then None else Some(cont x y)\n",
+ "\n",
+ "// remove this example for now and reference it when teaching the Option type\n",
+ "let tryDivide = combine (/) ``convert divBy0 to None``\n",
+ "\n",
+ "tryDivide 4 1 |> printfn \"%O\"\n",
+ "tryDivide 4 0 |> printfn \"%O\""
+ ]
+ },
{
"cell_type": "markdown",
"id": "7ec9b1fa",