|
1586 | 1586 | "id": "215f5f1e",
|
1587 | 1587 | "metadata": {},
|
1588 | 1588 | "source": [
|
1589 |
| - "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:" |
| 1589 | + "This feature is called **partial application**. It can help you express complexity using simple, modular pieces:" |
| 1590 | + ] |
| 1591 | + }, |
| 1592 | + { |
| 1593 | + "cell_type": "markdown", |
| 1594 | + "id": "80ae388c", |
| 1595 | + "metadata": {}, |
| 1596 | + "source": [ |
| 1597 | + "Here we build `add` and `divide` from `combine` by passing `+` and `/` to it. This code doesn't do much anything useful though... " |
1590 | 1598 | ]
|
1591 | 1599 | },
|
1592 | 1600 | {
|
1593 | 1601 | "cell_type": "code",
|
1594 |
| - "execution_count": 24, |
| 1602 | + "execution_count": 174, |
| 1603 | + "metadata": { |
| 1604 | + "dotnet_interactive": { |
| 1605 | + "language": "fsharp" |
| 1606 | + }, |
| 1607 | + "polyglot_notebook": { |
| 1608 | + "kernelName": "fsharp" |
| 1609 | + } |
| 1610 | + }, |
| 1611 | + "outputs": [ |
| 1612 | + { |
| 1613 | + "data": { |
| 1614 | + "text/html": [ |
| 1615 | + "<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", |
| 1616 | + ".dni-code-hint {\r\n", |
| 1617 | + " font-style: italic;\r\n", |
| 1618 | + " overflow: hidden;\r\n", |
| 1619 | + " white-space: nowrap;\r\n", |
| 1620 | + "}\r\n", |
| 1621 | + ".dni-treeview {\r\n", |
| 1622 | + " white-space: nowrap;\r\n", |
| 1623 | + "}\r\n", |
| 1624 | + ".dni-treeview td {\r\n", |
| 1625 | + " vertical-align: top;\r\n", |
| 1626 | + " text-align: start;\r\n", |
| 1627 | + "}\r\n", |
| 1628 | + "details.dni-treeview {\r\n", |
| 1629 | + " padding-left: 1em;\r\n", |
| 1630 | + "}\r\n", |
| 1631 | + "table td {\r\n", |
| 1632 | + " text-align: start;\r\n", |
| 1633 | + "}\r\n", |
| 1634 | + "table tr { \r\n", |
| 1635 | + " vertical-align: top; \r\n", |
| 1636 | + " margin: 0em 0px;\r\n", |
| 1637 | + "}\r\n", |
| 1638 | + "table tr td pre \r\n", |
| 1639 | + "{ \r\n", |
| 1640 | + " vertical-align: top !important; \r\n", |
| 1641 | + " margin: 0em 0px !important;\r\n", |
| 1642 | + "} \r\n", |
| 1643 | + "table th {\r\n", |
| 1644 | + " text-align: start;\r\n", |
| 1645 | + "}\r\n", |
| 1646 | + "</style>" |
| 1647 | + ] |
| 1648 | + }, |
| 1649 | + "metadata": {}, |
| 1650 | + "output_type": "display_data" |
| 1651 | + } |
| 1652 | + ], |
| 1653 | + "source": [ |
| 1654 | + "// y applied to x applied to f\n", |
| 1655 | + "let combine f x y = f x y\n", |
| 1656 | + "let add = combine (+) // a way to pass the + function\n", |
| 1657 | + "let divide = combine (/)\n", |
| 1658 | + "\n", |
| 1659 | + "add 3 4, divide 9 4" |
| 1660 | + ] |
| 1661 | + }, |
| 1662 | + { |
| 1663 | + "cell_type": "markdown", |
| 1664 | + "id": "2bbb29e9", |
| 1665 | + "metadata": {}, |
| 1666 | + "source": [ |
| 1667 | + "We can pass a `check` function to `combine` that can perform a check and decide whether we want to continue the computation or not." |
| 1668 | + ] |
| 1669 | + }, |
| 1670 | + { |
| 1671 | + "cell_type": "markdown", |
| 1672 | + "id": "92fbc8a2", |
| 1673 | + "metadata": {}, |
| 1674 | + "source": [ |
| 1675 | + "`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." |
| 1676 | + ] |
| 1677 | + }, |
| 1678 | + { |
| 1679 | + "cell_type": "code", |
| 1680 | + "execution_count": null, |
| 1681 | + "id": "ae36a578", |
| 1682 | + "metadata": { |
| 1683 | + "dotnet_interactive": { |
| 1684 | + "language": "fsharp" |
| 1685 | + }, |
| 1686 | + "polyglot_notebook": { |
| 1687 | + "kernelName": "fsharp" |
| 1688 | + } |
| 1689 | + }, |
| 1690 | + "outputs": [], |
| 1691 | + "source": [ |
| 1692 | + "let combine f check x y =\n", |
| 1693 | + " check f x y" |
| 1694 | + ] |
| 1695 | + }, |
| 1696 | + { |
| 1697 | + "cell_type": "markdown", |
| 1698 | + "id": "49329736", |
| 1699 | + "metadata": {}, |
| 1700 | + "source": [ |
| 1701 | + "We can build all different kinds of \"adders\" from `combine`:" |
| 1702 | + ] |
| 1703 | + }, |
| 1704 | + { |
| 1705 | + "cell_type": "code", |
| 1706 | + "execution_count": 213, |
1595 | 1707 | "id": "265d4712",
|
1596 | 1708 | "metadata": {
|
1597 | 1709 | "dotnet_interactive": {
|
|
1606 | 1718 | "name": "stdout",
|
1607 | 1719 | "output_type": "stream",
|
1608 | 1720 | "text": [
|
1609 |
| - "8\n", |
1610 |
| - "NaN\n" |
| 1721 | + "9\n", |
| 1722 | + "Adding 5 + 6...\n", |
| 1723 | + "11\n", |
| 1724 | + "8\n" |
1611 | 1725 | ]
|
1612 | 1726 | }
|
1613 | 1727 | ],
|
1614 | 1728 | "source": [
|
1615 |
| - "let combine f check x y =\n", |
1616 |
| - " f <|| check x y\n", |
| 1729 | + "let normalize f x y = f (abs x) (abs y)\n", |
1617 | 1730 | "\n",
|
1618 |
| - "let id2 x y = x, y\n", |
| 1731 | + "let normalizeThenAdd = combine (+) normalize\n", |
| 1732 | + "normalizeThenAdd -4 5 |> printfn \"%d\"\n", |
1619 | 1733 | "\n",
|
1620 |
| - "let add = combine (+) id2\n", |
1621 |
| - "add 3 5 |> printfn \"%d\"\n", |
| 1734 | + "let printThenAdd = combine (+) (fun f x y -> printfn \"Adding %d + %d...\" x y; f x y)\n", |
| 1735 | + "printThenAdd 5 6 |> printfn \"%d\"\n", |
1622 | 1736 | "\n",
|
1623 |
| - "let divideThen check = combine (/) check\n", |
1624 |
| - "let safeDivide = divideThen (fun x y -> if y = 0 then x, nan else x, y)\n", |
| 1737 | + "let add = combine (+) id // id is a special function that means \"do nothing\" in this context\n", |
| 1738 | + "add 3 5 |> printfn \"%d\"" |
| 1739 | + ] |
| 1740 | + }, |
| 1741 | + { |
| 1742 | + "cell_type": "markdown", |
| 1743 | + "id": "1c89cc9d", |
| 1744 | + "metadata": {}, |
| 1745 | + "source": [ |
| 1746 | + "We can build \"safe dividers\" that checks whether the denominator = 0." |
| 1747 | + ] |
| 1748 | + }, |
| 1749 | + { |
| 1750 | + "cell_type": "markdown", |
| 1751 | + "id": "1ca4528b", |
| 1752 | + "metadata": {}, |
| 1753 | + "source": [ |
| 1754 | + "`safeDivide` replaces `y` with `NaN` when `y = 0`:" |
| 1755 | + ] |
| 1756 | + }, |
| 1757 | + { |
| 1758 | + "cell_type": "code", |
| 1759 | + "execution_count": 206, |
| 1760 | + "metadata": { |
| 1761 | + "dotnet_interactive": { |
| 1762 | + "language": "fsharp" |
| 1763 | + }, |
| 1764 | + "polyglot_notebook": { |
| 1765 | + "kernelName": "fsharp" |
| 1766 | + } |
| 1767 | + }, |
| 1768 | + "outputs": [ |
| 1769 | + { |
| 1770 | + "name": "stdout", |
| 1771 | + "output_type": "stream", |
| 1772 | + "text": [ |
| 1773 | + "NaN\n" |
| 1774 | + ] |
| 1775 | + } |
| 1776 | + ], |
| 1777 | + "source": [ |
| 1778 | + "let ``convert divBy0 to NaN`` f x y =\n", |
| 1779 | + " f x (if y = 0 then nan else y)\n", |
1625 | 1780 | "\n",
|
| 1781 | + "let safeDivide = combine (/) ``convert divBy0 to NaN``\n", |
1626 | 1782 | "safeDivide 4 0 |> printfn \"%f\""
|
1627 | 1783 | ]
|
1628 | 1784 | },
|
| 1785 | + { |
| 1786 | + "cell_type": "markdown", |
| 1787 | + "id": "12c206eb", |
| 1788 | + "metadata": {}, |
| 1789 | + "source": [ |
| 1790 | + "`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", |
| 1791 | + "\n", |
| 1792 | + "`tryDivide` checks if `y = 0`, and if it is, it avoids doing the division altogether (by not evaluating `cont`).\n", |
| 1793 | + "> ℹ️ Note\n", |
| 1794 | + "> \n", |
| 1795 | + "> 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." |
| 1796 | + ] |
| 1797 | + }, |
| 1798 | + { |
| 1799 | + "cell_type": "code", |
| 1800 | + "execution_count": 219, |
| 1801 | + "metadata": { |
| 1802 | + "dotnet_interactive": { |
| 1803 | + "language": "fsharp" |
| 1804 | + }, |
| 1805 | + "polyglot_notebook": { |
| 1806 | + "kernelName": "fsharp" |
| 1807 | + } |
| 1808 | + }, |
| 1809 | + "outputs": [ |
| 1810 | + { |
| 1811 | + "name": "stdout", |
| 1812 | + "output_type": "stream", |
| 1813 | + "text": [ |
| 1814 | + "Some(4)\n", |
| 1815 | + "<null>\n" |
| 1816 | + ] |
| 1817 | + } |
| 1818 | + ], |
| 1819 | + "source": [ |
| 1820 | + "let ``convert divBy0 to None`` cont x y =\n", |
| 1821 | + " if y = 0 then None else Some(cont x y)\n", |
| 1822 | + "\n", |
| 1823 | + "// remove this example for now and reference it when teaching the Option type\n", |
| 1824 | + "let tryDivide = combine (/) ``convert divBy0 to None``\n", |
| 1825 | + "\n", |
| 1826 | + "tryDivide 4 1 |> printfn \"%O\"\n", |
| 1827 | + "tryDivide 4 0 |> printfn \"%O\"" |
| 1828 | + ] |
| 1829 | + }, |
1629 | 1830 | {
|
1630 | 1831 | "cell_type": "markdown",
|
1631 | 1832 | "id": "7ec9b1fa",
|
|
0 commit comments