Composing 0-argument functions
Maintainer antworten meist innerhalb von 1 Tag
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 52/100
Rechercherichtung
Beginne am Einstiegspunkt returns.functions.compose und vergleiche sein aktuelles Verhalten mit einem Argument mit den Beispielen ohne Argument im Issue. Überprüfe, wie die bestehende Callable-Typisierung ausgedrückt wird und wie komponierte Funktionen getestet werden. Als erledigt gilt die Aufgabe, wenn Callables ohne Argumente lazy komponiert werden, während die bestehende Komposition mit einem Argument kompatibel bleibt und das Verhalten durch Tests abgedeckt ist.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
I have a need to set up a function composition which is both lazy and takes zero arguments. Here is a somewhat contrived example from before I realized functions made by compose take exactly one argument no matter what:
import operator
import random
import returns.pointfree as fp
from returns.curry import partial
from returns.functions import compose
from returns.maybe import maybe
might_int = lambda: None if random.randint(0, 1) else 1
compose(
maybe(might_int),
pf.map_(partial(operator.add, 1))
)() # Expected: <Nothing> or <Some(2)>
# TypeError: compose.<locals>.<lambda>() missing 1 required positional argument: 'argument'
This example could easily be reworked with flow, of course. However, there may be cases where zero-argument functions need to be composed: any instance method with self as its only parameter, Context.get, partial or curried functions that are being passed around or reused in lots of places for their laziness, or any pattern that relies on "thunks" (trampolines and continuation passing come to mind).
(The actual use-case I have is a bit more complicated, involving decorator factories and such, and while I was typing up a description of it I found that the decorator factory already handles the very specific thing I was trying to do and I didn't need compose at all. I wrote that factory almost two years ago and completely forgot 😅 All the same, I feel like this is a gap in returns.)
I can think of three ways to accommodate this usage:
# Add a separate function just for this:
def compose0(
first: Callable[[], _SecondType],
second: Callable[[_SecondType], _ThirdType],
) -> Callable[[], _ThirdType]:
return lambda: second(first())
# Or, trade a couple microseconds for some flexibility:
import inspect
def compose[_ThirdType, **P](
first: Callable[P, _SecondType],
second: Callable[[_SecondType], _ThirdType],
) -> Callable[P, _ThirdType]:
param_count = len(inspect.signature(func).parameters) # May not always work; see edit below.
if param_count == 1:
return lambda argument: second(first(argument))
elif param_count == 0:
return lambda: second(first())
else:
# This helps by raising an error sooner, i.e. as soon as `compose` is called rather than when
# the produced lambda is called. This _could_ be the difference between catching a bug
# when the application starts instead of during runtime, having slipped through tests.
raise TypeError("The first function given to compose must be a 0- or 1-argument callable.")
# Or, total flexibility, and probably a strain on type checkers:
def compose[_ThirdType, **P](
first: Callable[P, _SecondType],
second: Callable[[_SecondType], _ThirdType],
) -> Callable[P, _ThirdType]:
def _composed(*args: P.args) -> _ThirdType:
return second(first(*args))
return _composed
Edit to add one aside:
There's an additional (if minor) advantage with the form of the third option--that is, returning a def function rather than a lambda function: Lambdas cannot be pickled as of Python 3.13, but regular functions can be. Most users are probably not bothered by this, admittedly, but it could become a complication for applications that also use eg. Celery, which has the option of using pickle as its data transfer protocol between actors.
One more:
I discovered that inspect.signature doesn't always play nicely with functions that are implemented in C and don't provide metadata. ContextVar.get happens to be one such function. I haven't yet found a good way to check if a function can be inspected this way other than to wrap it in a try/except.
- Vorherrschende Sprache
- Python
- Sterne
- 4.4k
- Forks
- 156
- Ø Merge
- 3 Std. 44 Min.
- Gemergte PRs (30 T.)
- 22
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus dry-python/returns
-
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 48/100
dry-python/returns#2394 · 1 Reaktion ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 25/100
dry-python/returns#2355 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 45/100
dry-python/returns#2295 · 4 Kommentare · 1 Reaktion ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 45/100
dry-python/returns#2253 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Incorrect result type for `returns.curry.partial` with keyword arguments for positional parametersOffenbug
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 45/100
dry-python/returns#2191 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in dry-python/returns
Ähnliche Issues
-
needs triage
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 83/100
darnitdevorg/darnit#587 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 67/100
Maintainer antworten meist innerhalb von 1 Tag
-
accessibility good first issue
Schwierigkeit 1/5 1-3 Stunden Anfängerfreundlichkeit 92/100
Maintainer antworten meist innerhalb von 1 Tag
-
llm translation
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
Maintainer antworten meist innerhalb von 1 Tag
-
bug bug-hunt
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
Maintainer antworten meist innerhalb von 1 Tag