You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: doc/features.md
+13-12Lines changed: 13 additions & 12 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -468,8 +468,8 @@ To rebind existing dynvars, use `dyn.k = v`, or `dyn.update(k0=v0, ...)`. Rebind
468
468
469
469
There is no `set` function or `<<` operator, unlike in the other `unpythonic` environments.
470
470
471
-
<details>
472
-
<summary>Each thread has its own dynamic scope stack. There is also a global dynamic scope for default values, shared between threads. </summary>
471
+
<details><summary>Each thread has its own dynamic scope stack. There is also a global dynamic scope for default values, shared between threads. </summary>
472
+
473
473
A newly spawned thread automatically copies the then-current state of the dynamic scope stack **from the main thread** (not the parent thread!). Any copied bindings will remain on the stack for the full dynamic extent of the new thread. Because these bindings are not associated with any `with` block running in that thread, and because aside from the initial copying, the dynamic scope stacks are thread-local, any copied bindings will never be popped, even if the main thread pops its own instances of them.
474
474
475
475
The source of the copy is always the main thread mainly because Python's `threading` module gives no tools to detect which thread spawned the current one. (If someone knows a simple solution, a PR is welcome!)
@@ -770,7 +770,7 @@ We also provide an **immutable** box, `Some`. This can be useful to represent op
770
770
771
771
The idea is that the value, when present, is placed into a `Some`, such as`Some(42)`, `Some("cat")`, `Some(myobject)`. Then, the situation where the value is absent can be represented as a bare `None`. So specifically, `Some(None)` means that a value is present and this value is`None`, whereas a bare `None` means that there is no value.
772
772
773
-
(It is like the `Some` constructor of a `Maybe` monad, but with no monadic magic. In this interpretation, the bare constant `None` plays the role of `Nothing`.)
773
+
It is like the `Some` constructor of a `Maybe` monad, but with no monadic magic. In this interpretation, the bare constant `None` plays the role of `Nothing`.
774
774
775
775
#### `ThreadLocalBox`
776
776
@@ -1292,7 +1292,7 @@ from unpythonic import lazy_piped, exitpipe
1292
1292
fibos= []
1293
1293
def nextfibo(a, b): # multiple arguments allowed
1294
1294
fibos.append(a) # store result by side effect
1295
-
# New state, handed to next function in the pipe.
1295
+
# New state, handed to the next function in the pipe.
1296
1296
# As of v0.15.0, use `Values(...)` to represent multiple return values.
1297
1297
# Positional args will be passed positionally, named ones by name.
1298
1298
return Values(a=b, b=(a + b))
@@ -1313,7 +1313,7 @@ Things missing from the standard library.
1313
1313
-`memoize`, with exception caching.
1314
1314
-`curry`, with passthrough like in Haskell.
1315
1315
-`fix`: detect andbreak infinite recursion cycles. **Added in v0.14.2.**
1316
-
-**Added in v0.15.0.**`partial`with run-time type checking, which helps a lot with fail-fast in code that uses partial application. This function type-checks arguments against type annotations, then delegates to `functools.partial`. Supports `unpythonic`'s `@generic` and `@typed` functions, too.
1316
+
-`partial`with run-time type checking, which helps a lot with fail-fast in code that uses partial application. This function type-checks arguments against type annotations, then delegates to `functools.partial`. Supports `unpythonic`'s `@generic` and `@typed` functions, too. **Added in v0.15.0.**
1317
1317
-`composel`, `composer`: both left-to-right and right-to-left function composition, to help readability.
1318
1318
-**Changed in v0.15.0.***For the benefit of code using the `with lazify` macro, the compose functions are now marked lazy. Arguments will be forced only when a lazy function in the chain actually uses them, or when an eager (not lazy) function is encountered in the chain.*
1319
1319
- Any number of positional and keyword arguments are supported, with the same rules asin the pipe system. Multiple return values, or named return values, represented as a `Values`, are automatically unpacked to the args and kwargs of the next function in the chain.
@@ -1505,7 +1505,7 @@ Our `curry` can be used both as a decorator and as a regular function. As a deco
1505
1505
1506
1506
Like Haskell, and [`spicy`for Racket](https://github.com/Technologicat/spicy), our `curry` supports *passthrough*; but we pass through **both positional and named arguments**.
1507
1507
1508
-
Any args and/or kwargs that are incompatible with the target function's call signature, are *passed through* in the sense that the function is called, and then its return value is merged with the remaining args and kwargs.
1508
+
Any args and/or kwargs that are incompatible with the target function's call signature, are *passed through* in the sense that the function is called with the args and kwargs compatible with its call signature, and then its return value is merged with the remaining args and kwargs.
1509
1509
1510
1510
If the *first positional return value* of the result of passthrough iscallable, it is (curried and) invoked on the remaining args and kwargs, after the merging. This helps with some instances of [point-free style](https://en.wikipedia.org/wiki/Tacit_programming).
1511
1511
@@ -2257,7 +2257,7 @@ The only differences are the name of the decorator and `return` vs. `yield from`
2257
2257
2258
2258
### `fup`: Functional update; `ShadowedSequence`
2259
2259
2260
-
**Changed in0.15.0.***Bug fixed: Now an infinite replacement sequence to pull items fromis actually ok, as the documentation has always claimed.*
2260
+
**Changed inv0.15.0.***Bug fixed: Now an infinite replacement sequence to pull items fromis actually ok, as the documentation has always claimed.*
2261
2261
2262
2262
We provide three layers, in increasing order of the level of abstraction: `ShadowedSequence`, `fupdate`, and`fup`.
2263
2263
@@ -2613,7 +2613,7 @@ Inspired by Haskell.
2613
2613
2614
2614
**Added in v0.14.2**.
2615
2615
2616
-
We provide **lispy symbols**, an **uninterned symbol generator**, and a **pythonic singleton abstraction**. These are all pickle-aware,and instantiation is thread-safe.
2616
+
We provide **lispy symbols**, an **uninterned symbol generator**, and a **pythonic singleton abstraction**. These are all pickle-aware and thread-safe.
2617
2617
2618
2618
#### Symbol
2619
2619
@@ -2637,7 +2637,7 @@ The function `gensym` creates an ***uninterned symbol***, also known as *a gensy
2637
2637
2638
2638
A gensym never conflicts withany named symbol; not even if one takes the UUIDfrom a gensym and creates a named symbol using that as the name.
2639
2639
2640
-
*The return value is the only time you'll see that symbol object; take good care of it!*
2640
+
*The return value of `gensym`is the only time you will see that particular uninterned symbol object; take good care of it!*
2641
2641
2642
2642
For example:
2643
2643
@@ -2693,11 +2693,11 @@ As the result of answering these questions, `unpythonic`'s idea of a singleton s
2693
2693
2694
2694
However, Python can easily retrieve a singleton instance with syntax that looks like regular object construction, by customizing [`__new__`](https://docs.python.org/3/reference/datamodel.html#object.__new__). Hence no static accessor method is needed. This in turn raises the question, what should we do with constructor arguments, as we surely would like to (in general) to allow those, and they can obviously differ between call sites. Since there is only one object instance to load state into, we could either silently update the state, or silently ignore the new proposed arguments. Good luck tracking down bugs either way. But upon closer inspection, that question depends on an unfounded assumption. What we should be asking instead is, *what should happen* if the constructor of a singleton is called again, while an instance already exists?
2695
2695
2696
-
We believe in the principles of [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) and [fail-fast](https://en.wikipedia.org/wiki/Fail-fast). The textbook singleton pattern conflates two concerns, possibly due to language limitations: the *management of object instances*, and the *enforcement of the at-most-one-instance-only guarantee*. If we wish to uncouple these responsibilities, then the obvious pythonic answer is that attempting to construct the singleton again while it already exists **should be considered a run-time error**. Since a singleton **type** does not support that operation, this situation should raise a `TypeError`. This makes the error explicit as early as possible, thus adhering to the fail-fast principle, hence making it difficult for bugs to hide (constructor arguments will either take effect, or the constructor call will explicitly fail).
2696
+
We believe in the principles of [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) and [fail-fast](https://en.wikipedia.org/wiki/Fail-fast). The textbook singleton pattern conflates two concerns, possibly due to language limitations: the *management of object instances*, and the *enforcement of the at-most-one-instance-only guarantee*. If we wish to uncouple these responsibilities, then the obvious pythonic answer is that attempting to construct the singleton again while it already exists **should be considered a run-time error**. Since a singleton **type** does not support that operation, this situation should raise a `TypeError`. This makes the error explicit as early as possible, thus adhering to the fail-fast principle, hence making it difficult for bugs to hide. Constructor arguments will either take effect, or the constructor call will explicitly fail.
2697
2697
2698
2698
Another question arises due to Python having builtin support forobject persistence, namely `pickle`. What *should* happen when a singleton is unpickled, while an instance of that singleton already exists? Arguably, by default, it should load the state from the pickle file into the existing instance, overwriting its current state.
2699
2699
2700
-
(Scenario: during second and later runs, a program first initializes, which causes the singleton instance to be created, just like during the first run of that program. Then the program loads state from a pickle file, containing (among other data) the state the singleton instance was in when the program previously shut down. In this scenario, considering the singleton, the data in the fileis more relevant than the defaults the program initialization feeds in. Hence the default should be to replace the state of the existing singleton instance with the data from the pickle file.)
2700
+
This design is based on considering the following scenario. During second and later runs, a program first initializes, which causes the singleton instance to be created, just like during the first run of that program. Then the program loads state from a pickle file, containing (among other data) the state the singleton instance was in when the program previously shut down. In this scenario, considering the singleton, the data in the fileis more relevant than the defaults the program initialization feeds in. Hence the default should be to replace the state of the existing singleton instance with the data from the pickle file.
2701
2701
2702
2702
Our `Singleton` abstraction is the result of these pythonifications applied to the classic pattern. For more documentation and examples, see the unit tests in [`unpythonic/tests/test_singleton.py`](../unpythonic/tests/test_singleton.py).
2703
2703
@@ -2721,7 +2721,7 @@ Most often, **don't**. `Singleton` is provided for the very rare occasion where
2721
2721
2722
2722
Cases 1and2 have no meaningful instance data. Case 3 may or may not, depending on the specifics. If your object does, andif you want it to support `pickle`, you may want to customize [`__getnewargs__`](https://docs.python.org/3/library/pickle.html#object.__getnewargs__) (called *at pickling time*), [`__setstate__`](https://docs.python.org/3/library/pickle.html#object.__setstate__), and sometimes maybe also [`__getstate__`](https://docs.python.org/3/library/pickle.html#object.__getstate__). Note that unpickling skips `__init__`, and calls just `__new__` (with the "newargs") and then `__setstate__`.
2723
2723
2724
-
I'm not completely sure if it's meaningful to provide a generic `Singleton` abstraction for Python, exceptfor teaching purposes. Practical use cases may differ so much, and some of the implementation details of the specific singleton object (esp. related to pickling) may depend so closely on the implementation details of the singleton abstraction, that it may be easier to just roll your own singleton code when needed. If you're new to customizing this part of Python, the code we have here should at least demonstrate an approach for how to do this.
2724
+
I am not completely sure if itismeaningful to provide a generic `Singleton` abstraction for Python, exceptfor teaching purposes. Practical use cases may differ so much, and some of the implementation details of the specific singleton object (especially related to pickling) may depend so closely on the implementation details of the singleton abstraction, that it may be easier to just roll your own singleton code when needed. If you are new to customizing this part of Python, the code we have here should at least demonstrate how to do that.
2725
2725
2726
2726
2727
2727
## Control flow tools
@@ -4335,6 +4335,7 @@ c = fixpoint(cos, x0=1)
4335
4335
4336
4336
# Actually "Newton's" algorithm for the square root was already known to the
4337
4337
# ancient Babylonians, ca. 2000 BCE. (Carl Boyer: History of mathematics)
4338
+
# Concerning naming, see also https://en.wikipedia.org/wiki/Stigler's_law_of_eponymy
4338
4339
def sqrt_newton(n):
4339
4340
def sqrt_iter(x): # has an attractive fixed point at sqrt(n)
0 commit comments