Skip to content

Commit fa920a8

Browse files
committed
various small doc wording fixes
1 parent 18590f3 commit fa920a8

File tree

7 files changed

+19
-16
lines changed

7 files changed

+19
-16
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ from itertools import repeat
491491
from unpythonic import fup
492492

493493
t = (1, 2, 3, 4, 5)
494-
s = fup(t)[0::2] << tuple(repeat(10, 3))
494+
s = fup(t)[0::2] << repeat(10)
495495
assert s == (10, 2, 10, 4, 10)
496496
assert t == (1, 2, 3, 4, 5)
497497
```

doc/features.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -468,8 +468,8 @@ To rebind existing dynvars, use `dyn.k = v`, or `dyn.update(k0=v0, ...)`. Rebind
468468

469469
There is no `set` function or `<<` operator, unlike in the other `unpythonic` environments.
470470

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+
473473
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.
474474

475475
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
770770

771771
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.
772772

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`.
774774

775775
#### `ThreadLocalBox`
776776

@@ -1292,7 +1292,7 @@ from unpythonic import lazy_piped, exitpipe
12921292
fibos = []
12931293
def nextfibo(a, b): # multiple arguments allowed
12941294
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.
12961296
# As of v0.15.0, use `Values(...)` to represent multiple return values.
12971297
# Positional args will be passed positionally, named ones by name.
12981298
return Values(a=b, b=(a + b))
@@ -1313,7 +1313,7 @@ Things missing from the standard library.
13131313
- `memoize`, with exception caching.
13141314
- `curry`, with passthrough like in Haskell.
13151315
- `fix`: detect and break 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.**
13171317
- `composel`, `composer`: both left-to-right and right-to-left function composition, to help readability.
13181318
- **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.*
13191319
- Any number of positional and keyword arguments are supported, with the same rules as in 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
15051505

15061506
Like Haskell, and [`spicy` for Racket](https://github.com/Technologicat/spicy), our `curry` supports *passthrough*; but we pass through **both positional and named arguments**.
15071507

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.
15091509

15101510
If the *first positional return value* of the result of passthrough is callable, 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).
15111511

@@ -2257,7 +2257,7 @@ The only differences are the name of the decorator and `return` vs. `yield from`
22572257
22582258
### `fup`: Functional update; `ShadowedSequence`
22592259
2260-
**Changed in 0.15.0.** *Bug fixed: Now an infinite replacement sequence to pull items from is actually ok, as the documentation has always claimed.*
2260+
**Changed in v0.15.0.** *Bug fixed: Now an infinite replacement sequence to pull items from is actually ok, as the documentation has always claimed.*
22612261
22622262
We provide three layers, in increasing order of the level of abstraction: `ShadowedSequence`, `fupdate`, and `fup`.
22632263
@@ -2613,7 +2613,7 @@ Inspired by Haskell.
26132613
26142614
**Added in v0.14.2**.
26152615
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.
26172617
26182618
#### Symbol
26192619
@@ -2637,7 +2637,7 @@ The function `gensym` creates an ***uninterned symbol***, also known as *a gensy
26372637
26382638
A gensym never conflicts with any named symbol; not even if one takes the UUID from a gensym and creates a named symbol using that as the name.
26392639
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!*
26412641
26422642
For example:
26432643
@@ -2693,11 +2693,11 @@ As the result of answering these questions, `unpythonic`'s idea of a singleton s
26932693
26942694
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?
26952695
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.
26972697
26982698
Another question arises due to Python having builtin support for object 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.
26992699
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 file is 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 file is 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.
27012701
27022702
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).
27032703
@@ -2721,7 +2721,7 @@ Most often, **don't**. `Singleton` is provided for the very rare occasion where
27212721
27222722
Cases 1 and 2 have no meaningful instance data. Case 3 may or may not, depending on the specifics. If your object does, and if 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__`.
27232723
2724-
I'm not completely sure if it's meaningful to provide a generic `Singleton` abstraction for Python, except for 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 it is meaningful to provide a generic `Singleton` abstraction for Python, except for 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.
27252725
27262726
27272727
## Control flow tools
@@ -4335,6 +4335,7 @@ c = fixpoint(cos, x0=1)
43354335
43364336
# Actually "Newton's" algorithm for the square root was already known to the
43374337
# 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
43384339
def sqrt_newton(n):
43394340
def sqrt_iter(x): # has an attractive fixed point at sqrt(n)
43404341
return (x + n / x) / 2

unpythonic/numutil.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ def fixpoint(f, x0, tol=0):
109109
110110
# Actually "Newton's" algorithm for the square root was already known to the
111111
# ancient Babylonians, ca. 2000 BCE. (Carl Boyer: History of mathematics)
112+
# Concerning naming, see also https://en.wikipedia.org/wiki/Stigler's_law_of_eponymy
112113
def sqrt_newton(n):
113114
def sqrt_iter(x): # has an attractive fixed point at sqrt(n)
114115
return (x + n / x) / 2

unpythonic/seq.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ def append_succ(lis):
251251
def nextfibo(state):
252252
a, b = state
253253
fibos.append(a) # store result by side effect
254-
return (b, a + b) # new state, handed to next function in the pipe
254+
return (b, a + b) # new state, handed to the next function in the pipe
255255
p = lazy_piped1((1, 1)) # load initial state into a lazy pipe
256256
for _ in range(10): # set up pipeline
257257
p = p | nextfibo

unpythonic/syntax/tests/test_lazify.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ def append_succ(lis):
589589
def nextfibo(state):
590590
a, b = state
591591
fibos.append(a) # store result by side effect
592-
return (b, a + b) # new state, handed to next function in the pipe
592+
return (b, a + b) # new state, handed to the next function in the pipe
593593
p = lazy_piped1((1, 1)) # load initial state into a lazy pipe
594594
for _ in range(10): # set up pipeline
595595
p = p | nextfibo

unpythonic/tests/test_numutil.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def runtests():
4747

4848
# Actually "Newton's" algorithm for the square root was already known to the
4949
# ancient Babylonians, ca. 2000 BCE. (Carl Boyer: History of mathematics)
50+
# Concerning naming, see also https://en.wikipedia.org/wiki/Stigler's_law_of_eponymy
5051
def sqrt_newton(n):
5152
def sqrt_iter(x): # has an attractive fixed point at sqrt(n)
5253
return (x + n / x) / 2

unpythonic/tests/test_seq.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def append_succ(lis):
118118
def nextfibo(state):
119119
a, b = state
120120
fibos.append(a) # store result by side effect
121-
return (b, a + b) # new state, handed to next function in the pipe
121+
return (b, a + b) # new state, handed to the next function in the pipe
122122
p = lazy_piped1((1, 1)) # load initial state into a lazy pipe
123123
for _ in range(10): # set up pipeline
124124
p = p | nextfibo

0 commit comments

Comments
 (0)