Spaces:
Running
Running
Commit
·
48feff6
1
Parent(s):
e3bcacf
refactor(applicatives): remove `sequenceL` from `Applicative` because it should be a classmethod but can't be generically implemented
Browse files
functional_programming/06_applicatives.py
CHANGED
|
@@ -7,9 +7,10 @@
|
|
| 7 |
|
| 8 |
import marimo
|
| 9 |
|
| 10 |
-
__generated_with = "0.12.
|
| 11 |
app = marimo.App(app_title="Applicative programming with effects")
|
| 12 |
|
|
|
|
| 13 |
@app.cell(hide_code=True)
|
| 14 |
def _(mo):
|
| 15 |
mo.md(
|
|
@@ -33,10 +34,9 @@ def _(mo):
|
|
| 33 |
/// details | Notebook metadata
|
| 34 |
type: info
|
| 35 |
|
| 36 |
-
version: 0.1.
|
| 37 |
|
| 38 |
///
|
| 39 |
-
|
| 40 |
"""
|
| 41 |
)
|
| 42 |
return
|
|
@@ -74,7 +74,7 @@ def _(mo):
|
|
| 74 |
def _(mo):
|
| 75 |
mo.md(
|
| 76 |
r"""
|
| 77 |
-
## Defining
|
| 78 |
|
| 79 |
As a result, we may want to define a single `Multifunctor` such that:
|
| 80 |
|
|
@@ -263,7 +263,7 @@ def _(mo):
|
|
| 263 |
|
| 264 |
- `apply` should apply a *wrapped* function to a *wrapper* value
|
| 265 |
|
| 266 |
-
The implementation is:
|
| 267 |
"""
|
| 268 |
)
|
| 269 |
return
|
|
@@ -379,7 +379,7 @@ def _(mo):
|
|
| 379 |
- if the function is `None`, apply returns `None`
|
| 380 |
- else apply the function to the value and wrap the result in `Just`
|
| 381 |
|
| 382 |
-
The implementation is:
|
| 383 |
"""
|
| 384 |
)
|
| 385 |
return
|
|
@@ -561,6 +561,10 @@ def _(mo):
|
|
| 561 |
r"""
|
| 562 |
## Utility functions
|
| 563 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 564 |
```python
|
| 565 |
@dataclass
|
| 566 |
class Applicative[A](Functor, ABC):
|
|
@@ -748,7 +752,7 @@ def _(
|
|
| 748 |
def _(mo):
|
| 749 |
mo.md(
|
| 750 |
r"""
|
| 751 |
-
# Effectful
|
| 752 |
|
| 753 |
Our original motivation for applicatives was the desire the generalise the idea of mapping to functions with multiple arguments. This is a valid interpretation of the concept of applicatives, but from the three instances we have seen it becomes clear that there is also another, more abstract view.
|
| 754 |
|
|
@@ -777,7 +781,7 @@ def _(mo):
|
|
| 777 |
|
| 778 |
- `apply` should perform an action that produces a function and perform an action that produces a value, then call the function with the value
|
| 779 |
|
| 780 |
-
The implementation is:
|
| 781 |
"""
|
| 782 |
)
|
| 783 |
return
|
|
@@ -829,69 +833,6 @@ def _():
|
|
| 829 |
return
|
| 830 |
|
| 831 |
|
| 832 |
-
@app.cell(hide_code=True)
|
| 833 |
-
def _(mo):
|
| 834 |
-
mo.md(r"""## Collect the sequence of response with sequenceL""")
|
| 835 |
-
return
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
@app.cell(hide_code=True)
|
| 839 |
-
def _(mo):
|
| 840 |
-
mo.md(
|
| 841 |
-
r"""
|
| 842 |
-
One often wants to execute a sequence of commands and collect the sequence of their response, and we can define a function `sequenceL` for this
|
| 843 |
-
|
| 844 |
-
/// admonition
|
| 845 |
-
sequenceL actually means that
|
| 846 |
-
|
| 847 |
-
> execute a list of commands and collect the list of their response
|
| 848 |
-
///
|
| 849 |
-
"""
|
| 850 |
-
)
|
| 851 |
-
return
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
@app.cell
|
| 855 |
-
def _(A, Applicative):
|
| 856 |
-
def sequenceL(actions: list[Applicative[A]], ap) -> Applicative[list[A]]:
|
| 857 |
-
if not actions:
|
| 858 |
-
return ap.pure([])
|
| 859 |
-
|
| 860 |
-
return ap.lift(
|
| 861 |
-
lambda: lambda l1: lambda: lambda l2: list(l1) + list(l2),
|
| 862 |
-
actions[0],
|
| 863 |
-
sequenceL(actions[1:], ap),
|
| 864 |
-
)
|
| 865 |
-
return (sequenceL,)
|
| 866 |
-
|
| 867 |
-
|
| 868 |
-
@app.cell(hide_code=True)
|
| 869 |
-
def _(mo):
|
| 870 |
-
mo.md(
|
| 871 |
-
r"""
|
| 872 |
-
This function transforms a list of applicative actions into a single such action that returns a list of result values, and captures a common pattern of applicative programming.
|
| 873 |
-
|
| 874 |
-
And we can rewrite the `get_chars` more concisely:
|
| 875 |
-
"""
|
| 876 |
-
)
|
| 877 |
-
return
|
| 878 |
-
|
| 879 |
-
|
| 880 |
-
@app.cell
|
| 881 |
-
def _(IO, sequenceL):
|
| 882 |
-
def get_chars_sequenceL(n: int = 3):
|
| 883 |
-
return sequenceL(
|
| 884 |
-
[IO.pure(input(f"input the {i}th str") for i in range(1, n + 1))], IO
|
| 885 |
-
)
|
| 886 |
-
return (get_chars_sequenceL,)
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
@app.cell
|
| 890 |
-
def _():
|
| 891 |
-
# print(get_chars_sequenceL()())
|
| 892 |
-
return
|
| 893 |
-
|
| 894 |
-
|
| 895 |
@app.cell(hide_code=True)
|
| 896 |
def _(mo):
|
| 897 |
mo.md(r"""# From the perspective of category theory""")
|
|
@@ -904,7 +845,7 @@ def _(mo):
|
|
| 904 |
r"""
|
| 905 |
## Lax Monoidal Functor
|
| 906 |
|
| 907 |
-
An alternative, equivalent formulation of `Applicative` is given by
|
| 908 |
"""
|
| 909 |
)
|
| 910 |
return
|
|
@@ -928,12 +869,6 @@ def _(ABC, Functor, abstractmethod, dataclass):
|
|
| 928 |
return (Monoidal,)
|
| 929 |
|
| 930 |
|
| 931 |
-
@app.cell
|
| 932 |
-
def _(mo):
|
| 933 |
-
mo.md(r"""fmap g fa = fmap (g . snd) (unit ** fa)""")
|
| 934 |
-
return
|
| 935 |
-
|
| 936 |
-
|
| 937 |
@app.cell(hide_code=True)
|
| 938 |
def _(mo):
|
| 939 |
mo.md(
|
|
@@ -943,7 +878,7 @@ def _(mo):
|
|
| 943 |
- `unit` provides the identity element
|
| 944 |
- `tensor` combines two contexts into a product context
|
| 945 |
|
| 946 |
-
More technically, the idea is that `monoidal functor` preserves the "monoidal structure" given by the pairing constructor `(,)` and unit type `()`.
|
| 947 |
"""
|
| 948 |
)
|
| 949 |
return
|
|
@@ -991,7 +926,7 @@ def _(mo):
|
|
| 991 |
def _(mo):
|
| 992 |
mo.md(
|
| 993 |
r"""
|
| 994 |
-
## Mutual
|
| 995 |
|
| 996 |
We can implement `pure` and `apply` in terms of `unit` and `tensor`, and vice versa.
|
| 997 |
|
|
|
|
| 7 |
|
| 8 |
import marimo
|
| 9 |
|
| 10 |
+
__generated_with = "0.12.4"
|
| 11 |
app = marimo.App(app_title="Applicative programming with effects")
|
| 12 |
|
| 13 |
+
|
| 14 |
@app.cell(hide_code=True)
|
| 15 |
def _(mo):
|
| 16 |
mo.md(
|
|
|
|
| 34 |
/// details | Notebook metadata
|
| 35 |
type: info
|
| 36 |
|
| 37 |
+
version: 0.1.1 | last modified: 2025-04-06 | author: [métaboulie](https://github.com/metaboulie)<br/>
|
| 38 |
|
| 39 |
///
|
|
|
|
| 40 |
"""
|
| 41 |
)
|
| 42 |
return
|
|
|
|
| 74 |
def _(mo):
|
| 75 |
mo.md(
|
| 76 |
r"""
|
| 77 |
+
## Defining Multifunctor
|
| 78 |
|
| 79 |
As a result, we may want to define a single `Multifunctor` such that:
|
| 80 |
|
|
|
|
| 263 |
|
| 264 |
- `apply` should apply a *wrapped* function to a *wrapper* value
|
| 265 |
|
| 266 |
+
The implementation is:
|
| 267 |
"""
|
| 268 |
)
|
| 269 |
return
|
|
|
|
| 379 |
- if the function is `None`, apply returns `None`
|
| 380 |
- else apply the function to the value and wrap the result in `Just`
|
| 381 |
|
| 382 |
+
The implementation is:
|
| 383 |
"""
|
| 384 |
)
|
| 385 |
return
|
|
|
|
| 561 |
r"""
|
| 562 |
## Utility functions
|
| 563 |
|
| 564 |
+
/// attention
|
| 565 |
+
`fmap` is defined automatically using `pure` and `apply`, so you can use `fmap` with any `Applicative`
|
| 566 |
+
///
|
| 567 |
+
|
| 568 |
```python
|
| 569 |
@dataclass
|
| 570 |
class Applicative[A](Functor, ABC):
|
|
|
|
| 752 |
def _(mo):
|
| 753 |
mo.md(
|
| 754 |
r"""
|
| 755 |
+
# Effectful programming
|
| 756 |
|
| 757 |
Our original motivation for applicatives was the desire the generalise the idea of mapping to functions with multiple arguments. This is a valid interpretation of the concept of applicatives, but from the three instances we have seen it becomes clear that there is also another, more abstract view.
|
| 758 |
|
|
|
|
| 781 |
|
| 782 |
- `apply` should perform an action that produces a function and perform an action that produces a value, then call the function with the value
|
| 783 |
|
| 784 |
+
The implementation is:
|
| 785 |
"""
|
| 786 |
)
|
| 787 |
return
|
|
|
|
| 833 |
return
|
| 834 |
|
| 835 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
@app.cell(hide_code=True)
|
| 837 |
def _(mo):
|
| 838 |
mo.md(r"""# From the perspective of category theory""")
|
|
|
|
| 845 |
r"""
|
| 846 |
## Lax Monoidal Functor
|
| 847 |
|
| 848 |
+
An alternative, equivalent formulation of `Applicative` is given by
|
| 849 |
"""
|
| 850 |
)
|
| 851 |
return
|
|
|
|
| 869 |
return (Monoidal,)
|
| 870 |
|
| 871 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 872 |
@app.cell(hide_code=True)
|
| 873 |
def _(mo):
|
| 874 |
mo.md(
|
|
|
|
| 878 |
- `unit` provides the identity element
|
| 879 |
- `tensor` combines two contexts into a product context
|
| 880 |
|
| 881 |
+
More technically, the idea is that `monoidal functor` preserves the "monoidal structure" given by the pairing constructor `(,)` and unit type `()`.
|
| 882 |
"""
|
| 883 |
)
|
| 884 |
return
|
|
|
|
| 926 |
def _(mo):
|
| 927 |
mo.md(
|
| 928 |
r"""
|
| 929 |
+
## Mutual definability of Monoidal and Applicative
|
| 930 |
|
| 931 |
We can implement `pure` and `apply` in terms of `unit` and `tensor`, and vice versa.
|
| 932 |
|
functional_programming/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
| 1 |
# Changelog of the functional-programming course
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
## 2025-04-02
|
| 4 |
|
| 5 |
* `0.1.0` version of notebook `06_applicatives.py`
|
|
|
|
| 1 |
# Changelog of the functional-programming course
|
| 2 |
|
| 3 |
+
## 2025-04-06
|
| 4 |
+
|
| 5 |
+
- remove `sequenceL` from `Applicative` because it should be a classmethod but can't be generically implemented
|
| 6 |
+
|
| 7 |
## 2025-04-02
|
| 8 |
|
| 9 |
* `0.1.0` version of notebook `06_applicatives.py`
|