metaboulie commited on
Commit
6399834
·
1 Parent(s): 397edde

refactor(functors): `v0.1.5` of `functors.py`

Browse files
functional_programming/05_functors.py CHANGED
@@ -7,8 +7,12 @@
7
 
8
  import marimo
9
 
10
- __generated_with = "0.12.5"
11
- app = marimo.App(app_title="Category Theory and Functors")
 
 
 
 
12
 
13
  @app.cell(hide_code=True)
14
  def _(mo):
@@ -36,7 +40,7 @@ def _(mo):
36
  /// details | Notebook metadata
37
  type: info
38
 
39
- version: 0.1.4 | last modified: 2025-04-08 | author: [métaboulie](https://github.com/metaboulie)<br/>
40
  reviewer: [Haleshot](https://github.com/Haleshot)
41
 
42
  ///
@@ -354,7 +358,7 @@ def _(mo):
354
 
355
  `fmap` for `Either` will ignore Left values, but will apply the supplied function to values contained in the Right.
356
 
357
- The implementation is:
358
  """
359
  )
360
  return
@@ -707,7 +711,7 @@ def _(ABC, B, Callable, abstractmethod, dataclass):
707
  @classmethod
708
  @abstractmethod
709
  def fmap(cls, g: Callable[[A], B], fa: "Functor[A]") -> "Functor[B]":
710
- return NotImplementedError
711
 
712
  @classmethod
713
  def const(cls, fa: "Functor[A]", b: B) -> "Functor[B]":
@@ -912,8 +916,13 @@ def _(mo):
912
 
913
  Once again there are a few axioms that functors have to obey.
914
 
915
- 1. Given an identity morphism $id_A$ on an object $A$, $F ( id_A )$ must be the identity morphism on $F ( A )$, i.e.: $F({id} _{A})={id} _{F(A)}$
916
- 2. Functors must distribute over morphism composition, i.e. $F(f\circ g)=F(f)\circ F(g)$
 
 
 
 
 
917
  """
918
  )
919
  return
@@ -1205,6 +1214,187 @@ def _(mo):
1205
  return
1206
 
1207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1208
  @app.cell(hide_code=True)
1209
  def _(mo):
1210
  mo.md(
@@ -1253,7 +1443,8 @@ def _(TypeVar):
1253
  A = TypeVar("A")
1254
  B = TypeVar("B")
1255
  C = TypeVar("C")
1256
- return A, B, C
 
1257
 
1258
 
1259
  if __name__ == "__main__":
 
7
 
8
  import marimo
9
 
10
+ __generated_with = "0.12.8"
11
+ app = marimo.App(
12
+ app_title="Category Theory and Functors",
13
+ css_file="/Users/chanhuizhihou/Library/Application Support/mtheme/themes/gruvbox.css",
14
+ )
15
+
16
 
17
  @app.cell(hide_code=True)
18
  def _(mo):
 
40
  /// details | Notebook metadata
41
  type: info
42
 
43
+ version: 0.1.5 | last modified: 2025-04-11 | author: [métaboulie](https://github.com/metaboulie)<br/>
44
  reviewer: [Haleshot](https://github.com/Haleshot)
45
 
46
  ///
 
358
 
359
  `fmap` for `Either` will ignore Left values, but will apply the supplied function to values contained in the Right.
360
 
361
+ The implementation is:
362
  """
363
  )
364
  return
 
711
  @classmethod
712
  @abstractmethod
713
  def fmap(cls, g: Callable[[A], B], fa: "Functor[A]") -> "Functor[B]":
714
+ raise NotImplementedError("Subclasses must implement fmap")
715
 
716
  @classmethod
717
  def const(cls, fa: "Functor[A]", b: B) -> "Functor[B]":
 
916
 
917
  Once again there are a few axioms that functors have to obey.
918
 
919
+ 1. Given an identity morphism $id_A$ on an object $A$, $F ( id_A )$ must be the identity morphism on $F ( A )$.:
920
+
921
+ $$F({id} _{A})={id} _{F(A)}$$
922
+
923
+ 3. Functors must distribute over morphism composition.
924
+
925
+ $$F(f\circ g)=F(f)\circ F(g)$$
926
  """
927
  )
928
  return
 
1214
  return
1215
 
1216
 
1217
+ @app.cell(hide_code=True)
1218
+ def _(mo):
1219
+ mo.md(
1220
+ r"""
1221
+ # Bifunctor
1222
+
1223
+ A `Bifunctor` is a type constructor that takes two type arguments and **is a functor in both arguments.**
1224
+
1225
+ For example, think about `Either`'s usual `Functor` instance. It only allows you to fmap over the second type parameter: `right` values get mapped, `left` values stay as they are.
1226
+
1227
+ However, its `Bifunctor` instance allows you to map both halves of the sum.
1228
+
1229
+ There are three core methods for `Bifunctor`:
1230
+
1231
+ - `bimap` allows mapping over both type arguments at once.
1232
+ - `first` and `second` are also provided for mapping over only one type argument at a time.
1233
+
1234
+
1235
+ The abstraction of `Bifunctor` is:
1236
+ """
1237
+ )
1238
+ return
1239
+
1240
+
1241
+ @app.cell
1242
+ def _(ABC, B, Callable, D, dataclass, f, id):
1243
+ @dataclass
1244
+ class Bifunctor[A, C](ABC):
1245
+ @classmethod
1246
+ def bimap(
1247
+ cls, g: Callable[[A], B], h: Callable[[C], D], fa: "Bifunctor[A, C]"
1248
+ ) -> "Bifunctor[B, D]":
1249
+ return cls.first(f, cls.second(g, fa))
1250
+
1251
+ @classmethod
1252
+ def first(
1253
+ cls, g: Callable[[A], B], fa: "Bifunctor[A, C]"
1254
+ ) -> "Bifunctor[B, C]":
1255
+ return cls.bimap(g, id, fa)
1256
+
1257
+ @classmethod
1258
+ def second(
1259
+ cls, g: Callable[[B], C], fa: "Bifunctor[A, B]"
1260
+ ) -> "Bifunctor[A, C]":
1261
+ return cls.bimap(id, g, fa)
1262
+ return (Bifunctor,)
1263
+
1264
+
1265
+ @app.cell(hide_code=True)
1266
+ def _(mo):
1267
+ mo.md(
1268
+ r"""
1269
+ /// admonition | minimal implementation requirement
1270
+ - `bimap` or both `first` and `second`
1271
+ ///
1272
+ """
1273
+ )
1274
+ return
1275
+
1276
+
1277
+ @app.cell(hide_code=True)
1278
+ def _(mo):
1279
+ mo.md(r"""## Instances of Bifunctor""")
1280
+ return
1281
+
1282
+
1283
+ @app.cell(hide_code=True)
1284
+ def _(mo):
1285
+ mo.md(
1286
+ r"""
1287
+ ### The Either Bifunctor
1288
+
1289
+ For the `Either Bifunctor`, we allow it to map a function over the `left` value as well.
1290
+
1291
+ Notice that, the `Either Bifunctor` still only contains the `left` value or the `right` value.
1292
+ """
1293
+ )
1294
+ return
1295
+
1296
+
1297
+ @app.cell
1298
+ def _(B, Bifunctor, Callable, D, dataclass):
1299
+ @dataclass
1300
+ class BiEither[A, C](Bifunctor):
1301
+ left: A = None
1302
+ right: C = None
1303
+
1304
+ def __post_init__(self):
1305
+ if (self.left is not None and self.right is not None) or (
1306
+ self.left is None and self.right is None
1307
+ ):
1308
+ raise TypeError(
1309
+ "Provide either the value of the left or the value of the right."
1310
+ )
1311
+
1312
+ @classmethod
1313
+ def bimap(
1314
+ cls, g: Callable[[A], B], h: Callable[[C], D], fa: "BiEither[A, C]"
1315
+ ) -> "BiEither[B, D]":
1316
+ if fa.left is not None:
1317
+ return cls(left=g(fa.left))
1318
+ return cls(right=h(fa.right))
1319
+
1320
+ def __repr__(self):
1321
+ if self.left is not None:
1322
+ return f"Left({self.left!r})"
1323
+ return f"Right({self.right!r})"
1324
+ return (BiEither,)
1325
+
1326
+
1327
+ @app.cell
1328
+ def _(BiEither):
1329
+ print(BiEither.bimap(lambda x: x + 1, lambda x: x * 2, BiEither(left=1)))
1330
+ print(BiEither.bimap(lambda x: x + 1, lambda x: x * 2, BiEither(right=2)))
1331
+ print(BiEither.first(lambda x: x + 1, BiEither(left=1)))
1332
+ print(BiEither.first(lambda x: x + 1, BiEither(right=2)))
1333
+ print(BiEither.second(lambda x: x + 1, BiEither(left=1)))
1334
+ print(BiEither.second(lambda x: x + 1, BiEither(right=2)))
1335
+ return
1336
+
1337
+
1338
+ @app.cell(hide_code=True)
1339
+ def _(mo):
1340
+ mo.md(
1341
+ r"""
1342
+ ### The 2d Tuple Bifunctor
1343
+
1344
+ For 2d tuples, we simply expect `bimap` to map 2 functions to the 2 elements in the tuple respectively.
1345
+ """
1346
+ )
1347
+ return
1348
+
1349
+
1350
+ @app.cell
1351
+ def _(B, Bifunctor, Callable, D, dataclass):
1352
+ @dataclass
1353
+ class BiTuple[A, C](Bifunctor):
1354
+ value: tuple[A, C]
1355
+
1356
+ @classmethod
1357
+ def bimap(
1358
+ cls, g: Callable[[A], B], h: Callable[[C], D], fa: "BiTuple[A, C]"
1359
+ ) -> "BiTuple[B, D]":
1360
+ return cls((g(fa.value[0]), h(fa.value[1])))
1361
+ return (BiTuple,)
1362
+
1363
+
1364
+ @app.cell
1365
+ def _(BiTuple):
1366
+ print(BiTuple.bimap(lambda x: x + 1, lambda x: x * 2, BiTuple((1, 2))))
1367
+ print(BiTuple.first(lambda x: x + 1, BiTuple((1, 2))))
1368
+ print(BiTuple.second(lambda x: x + 1, BiTuple((1, 2))))
1369
+ return
1370
+
1371
+
1372
+ @app.cell(hide_code=True)
1373
+ def _(mo):
1374
+ mo.md(
1375
+ r"""
1376
+ ## Bifunctor laws
1377
+
1378
+ The only law we need to follow is
1379
+
1380
+ ```python
1381
+ bimap(id, id, fa) == id(fa)
1382
+ ```
1383
+
1384
+ and then other laws are followed automatically.
1385
+ """
1386
+ )
1387
+ return
1388
+
1389
+
1390
+ @app.cell
1391
+ def _(BiEither, BiTuple, id):
1392
+ print(BiEither.bimap(id, id, BiEither(left=1)) == id(BiEither(left=1)))
1393
+ print(BiEither.bimap(id, id, BiEither(right=1)) == id(BiEither(right=1)))
1394
+ print(BiTuple.bimap(id, id, BiTuple((1, 2))) == id(BiTuple((1, 2))))
1395
+ return
1396
+
1397
+
1398
  @app.cell(hide_code=True)
1399
  def _(mo):
1400
  mo.md(
 
1443
  A = TypeVar("A")
1444
  B = TypeVar("B")
1445
  C = TypeVar("C")
1446
+ D = TypeVar("D")
1447
+ return A, B, C, D
1448
 
1449
 
1450
  if __name__ == "__main__":
functional_programming/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
  # Changelog of the functional-programming course
2
 
 
 
 
 
 
 
 
3
  ## 2025-04-08
4
 
5
  **functors.py**
 
1
  # Changelog of the functional-programming course
2
 
3
+ ## 2025-04-11
4
+
5
+ **functors.py**
6
+
7
+ + add `Bifunctor` section
8
+ * replace `return NotImplementedError` with `raise NotImplementedError`
9
+
10
  ## 2025-04-08
11
 
12
  **functors.py**
functional_programming/README.md CHANGED
@@ -3,20 +3,20 @@
3
  _🚧 This collection is a [work in progress](https://github.com/marimo-team/learn/issues/51)._
4
 
5
  This series of marimo notebooks introduces the powerful paradigm of functional
6
- programming through Python. Taking inspiration from Haskell and Category Theory,
7
- we'll build a strong foundation in FP concepts that can transform how you
8
- approach software development.
9
 
10
  ## What You'll Learn
11
 
12
- **Using only Python's standard library**, we'll construct functional programming
13
- concepts from first principles.
14
 
15
  Topics include:
16
 
17
- - Currying and higher-order functions
18
- - Functors, Applicatives, and Monads
19
- - Category theory fundamentals
20
 
21
  ## Running Notebooks
22
 
@@ -24,20 +24,21 @@ Topics include:
24
 
25
  To run a notebook locally, use
26
 
27
- ```bash
28
- uvx marimo edit <URL>
29
  ```
30
 
31
  For example, run the `Functor` tutorial with
32
 
33
- ```bash
34
  uvx marimo edit https://github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py
35
  ```
36
 
37
  ### On Our Online Playground
38
 
39
  You can also open notebooks in our online playground by appending `marimo.app/` to a notebook's URL like:
40
- [marimo.app/github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py](https://marimo.app/https://github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py).
 
41
 
42
  ### On Our Landing Page
43
 
@@ -50,17 +51,19 @@ on Discord (@eugene.hs).
50
 
51
  # Description of notebooks
52
 
53
- Check [here](https://github.com/marimo-team/learn/issues/51) for current series structure.
54
- | Notebook | Title | Description | Key Concepts | Prerequisites |
55
- |----------|-------|-------------|--------------|---------------|
56
- | [05. Functors](https://github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py) | Category and Functors | Learn why `len` is a _Functor_ from `list concatenation` to `integer addition`, how to _lift_ an ordinary function into a _computation context_, and how to write an _adapter_ between two categories. | Categories, Functors, Function lifting, Context mapping | Basic Python, Functions |
57
- | [06. Applicatives](https://github.com/marimo-team/learn/blob/main/functional_programming/06_applicatives.py) | Applicative programming with effects | Learn how to apply functions within a context, combining multiple effects in a pure way. Learn about the `pure` and `apply` operations that make applicatives powerful for handling multiple computations. | Applicative Functors, Pure, Apply, Effectful programming | Functors |
 
 
58
 
59
  **Authors.**
60
 
61
  Thanks to all our notebook authors!
62
 
63
- - [métaboulie](https://github.com/metaboulie)
64
 
65
  **Reviewers.**
66
 
 
3
  _🚧 This collection is a [work in progress](https://github.com/marimo-team/learn/issues/51)._
4
 
5
  This series of marimo notebooks introduces the powerful paradigm of functional
6
+ programming through Python. Taking inspiration from Haskell and Category
7
+ Theory, we'll build a strong foundation in FP concepts that can transform how
8
+ you approach software development.
9
 
10
  ## What You'll Learn
11
 
12
+ **Using only Python's standard library**, we'll construct functional
13
+ programming concepts from first principles.
14
 
15
  Topics include:
16
 
17
+ + Currying and higher-order functions
18
+ + Functors, Applicatives, and Monads
19
+ + Category theory fundamentals
20
 
21
  ## Running Notebooks
22
 
 
24
 
25
  To run a notebook locally, use
26
 
27
+ ```bash
28
+ uvx marimo edit <URL>
29
  ```
30
 
31
  For example, run the `Functor` tutorial with
32
 
33
+ ```bash
34
  uvx marimo edit https://github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py
35
  ```
36
 
37
  ### On Our Online Playground
38
 
39
  You can also open notebooks in our online playground by appending `marimo.app/` to a notebook's URL like:
40
+
41
+ https://marimo.app/https://github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py
42
 
43
  ### On Our Landing Page
44
 
 
51
 
52
  # Description of notebooks
53
 
54
+ Check [here](https://github.com/marimo-team/learn/issues/51) for current series
55
+ structure.
56
+
57
+ | Notebook | Title | Key Concepts | Prerequisites |
58
+ |----------|-------|--------------|---------------|
59
+ | [05. Functors](https://github.com/marimo-team/learn/blob/main/functional_programming/05_functors.py) | Category Theory and Functors | Category Theory, Functors, fmap | Basic Python, Functions |
60
+ | [06. Applicatives](https://github.com/marimo-team/learn/blob/main/functional_programming/06_applicatives.py) | Applicative programming with effects | Applicative Functors, pure, apply, Effectful programming, Alternatives | Functors |
61
 
62
  **Authors.**
63
 
64
  Thanks to all our notebook authors!
65
 
66
+ - [métaboulie](https://github.com/metaboulie)
67
 
68
  **Reviewers.**
69