Skip to content

Commit

Permalink
Updated Partial Application section
Browse files Browse the repository at this point in the history
Rewrote last example
  • Loading branch information
johnW-ret committed Apr 17, 2024
1 parent e164d1d commit 38761a8
Showing 1 changed file with 212 additions and 11 deletions.
223 changes: 212 additions & 11 deletions index.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
"<details open=\"open\" class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>(7, 2)</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>Item1</td><td><div class=\"dni-plaintext\"><pre>7</pre></div></td></tr><tr><td>Item2</td><td><div class=\"dni-plaintext\"><pre>2</pre></div></td></tr></tbody></table></div></details><style>\r\n",
".dni-code-hint {\r\n",
" font-style: italic;\r\n",
" overflow: hidden;\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview {\r\n",
" white-space: nowrap;\r\n",
"}\r\n",
".dni-treeview td {\r\n",
" vertical-align: top;\r\n",
" text-align: start;\r\n",
"}\r\n",
"details.dni-treeview {\r\n",
" padding-left: 1em;\r\n",
"}\r\n",
"table td {\r\n",
" text-align: start;\r\n",
"}\r\n",
"table tr { \r\n",
" vertical-align: top; \r\n",
" margin: 0em 0px;\r\n",
"}\r\n",
"table tr td pre \r\n",
"{ \r\n",
" vertical-align: top !important; \r\n",
" margin: 0em 0px !important;\r\n",
"} \r\n",
"table th {\r\n",
" text-align: start;\r\n",
"}\r\n",
"</style>"
]
},
"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": {
Expand All @@ -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",
"<null>\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",
Expand Down

0 comments on commit 38761a8

Please sign in to comment.