pythonic-fp.fptools API¶
FP Tools Package¶
Pythonic FP - Functional Programming Tools
Tools to aid with functional programming in Python yet still endeavoring to remain Pythonic.
Subtypeable Boolean¶
Class Bool - Subclassable Boolean
Python does not permit bool to be subclassed, but int can be subclassed.
Under-the-hood a bool is just an int. The Bool class inherits from
int and relies on the underlying truthiness and falsiness
of 1 and 0.
The Truth(truth: str) and Lie(lie: str) subclass constructors
produce singletons based on their input parameters. When using type hints,
declare variables of these types as type Bool. Best practices when
used with these subclasses are:
use == or != for pure Boolean comparisons
use is or not is if the type of truth matters
only use Bool() as a type, never as a constructor
when using Python shortcut logic remember
an instance of
Truthis truthyan instance of
Lieis falsyshortcut logic is lazy
the last truthy thing evaluated is returned
and is not converted to a
bool
the not statement converts a
Boolto an actualbool
- class pythonic_fp.fptools.bool.Bool¶
Subclassable Boolean-like class.
- class pythonic_fp.fptools.bool.Lie(lie='LIE')¶
Falsy singleton Bool subclass.
- :: note:
When using type hints, declare variables Bool, not Lie.
- class pythonic_fp.fptools.bool.Truth(truth='TRUTH')¶
Truthy singleton Bool subclass.
- :: note:
When using type hints, declare variables Bool, not Truth.
Function Tools¶
Pythonic FP - FP tools for functions
Not a replacement for the std library’s functools which is more about modifying function behavior through decorators than functional composition and application.
FP utilities to manipulate and partially apply functions
function swap: Swap the arguments of a 2 argument function
function it: Function returning an iterator of its arguments
function sequenced: Convert function to take a sequence of its arguments
function negate: Transforms a predicate to its negation
function partial: Returns a partially applied function
- pythonic_fp.fptools.function.it(*args)¶
Function returning an iterator of its arguments.
- Return type:
Iterator[A]
- pythonic_fp.fptools.function.negate(f)¶
Take a predicate and return its negation.
- Return type:
Callable[ParamSpec,bool]
- pythonic_fp.fptools.function.partial(f, *args)¶
Partially apply arguments to a function, left to right.
type-wise the only thing guaranteed is the return type
best practice is to cast the result immediately
- Return type:
Callable[…,R]
- pythonic_fp.fptools.function.sequenced(f)¶
Convert a function with arbitrary positional arguments to one taking a tuple of the original arguments.
was awaiting typing and mypy “improvements” to ParamSpec
return type: Callable[tuple[P.args], R] ???
return type: Callable[[tuple[P.args]], R] ???
not going to happen - https://github.com/python/mypy/pull/18278
TODO: Look into replacing this function with a Callable class?
- Return type:
Callable[tuple[Any],R]
- pythonic_fp.fptools.function.swap(f)¶
Swap arguments of a two argument function.
- Return type:
Callable[V,U,R]
Lazy Function Evaluation¶
Pythonic FP - Lazy function evaluation
Delayed function evaluations. FP tools for “non-strict” function evaluations. Useful to delay a function’s evaluation until some inner scope.
Non-strict delayed function evaluation:
class Lazy: Delay evaluation of functions taking & returning single values
function lazy: Delay evaluation of functions taking any number of values
function real_lazy: Version of
lazywhich caches its result
- class pythonic_fp.fptools.lazy.Lazy(f, d, pure=True)¶
Delayed evaluation of a singled valued function.
Class instance delays the executable of a function where
Lazy(f, arg)constructs an object that can evaluate the Callablefwith its argument at a later time.first argument
ftaking values of type~Dto values of type~Rsecond argument
arg: ~Dis the argument to be passed tofwhere the type
~Dis thetupletype of the argument types tof
function is evaluated when the
evalmethod is calledresult is cached unless
pureis set toFalsereturns True in Boolean context if evaluated
Usually use case is to make a function “non-strict” by passing some of its arguments wrapped in Lazy instances.
- eval()¶
Evaluate function with its argument.
evaluate function
cache result or exception if
pure == Truereevaluate if
pure == False
- Return type:
None
- get(alt=None)¶
Get result only if evaluated and no exceptions occurred, otherwise return an alternate value.
A possible use case would be if the calculation is expensive, but if it has already been done, its result is better than the alternate value.
- Return type:
Union[-R,Never]
- get_exception()¶
Get result only if evaluate and exceptional.
- Return type:
MayBe[Exception]
- get_result()¶
Get result only if evaluated and not exceptional.
- Return type:
MayBe[-R]
- got_exception()¶
Return true if Lazy raised exception.
- Return type:
MayBe[bool]
- got_result()¶
Return true if an evaluated Lazy did not raise an exception.
- Return type:
MayBe[bool]
- pythonic_fp.fptools.lazy.lazy(f, *args, **kwargs)¶
Delayed evaluation of a function with arbitrary positional arguments.
Function returning a delayed evaluation of a function of an arbitrary number of positional arguments.
first positional argument
ftakes a functionnext positional arguments are the arguments to be applied later to
ffis reevaluated wheneverevalmethod of the returnedLazyis called
any kwargs passed are ignored
if
fneeds them, then wrapfin another function
- Return type:
Lazy[tuple[Any, …],R]
- pythonic_fp.fptools.lazy.real_lazy(f, *args, **kwargs)¶
Cached delayed evaluation of a function with arbitrary positional arguments.
Function returning a delayed evaluation of a function of an arbitrary number of positional arguments.
first positional argument
ftakes a functionnext positional arguments are the arguments to be applied later to
ffis evaluated whenevalmethod of the returnedLazyis calledfis evaluated only once with results cached
any kwargs passed are ignored
if
fneeds them then wrapfin another function
- Return type:
Lazy[tuple[Any, …],R]
Singletons¶
Pythonic FP - Collection of singleton classes
- final class pythonic_fp.fptools.singletons.Nada¶
Singleton class representing & propagating failure.
singleton
_nada: nada = Nada()represents a non-existent valuereturns itself for arbitrary method calls
returns itself if called as a Callable with arbitrary arguments
interpreted as an empty container by standard Python functions
warning: non-standard equality semantics
comparison compares true only when 2 non-missing values compare true
thus
a == bmeans two non-missing values compare as equal
usage
import
Nadaand theneither use
Nada()directlyor define
_nada: Final[Nada] = Nada()don’t export it
start propagating failure by setting a propagating value to Nada()
works best when working with expression
failure may fail to propagate
for a function/method with just side effects
engineer Nada() to fail to trigger side effects
test for failure by comparing a result to
Nada()itself usingisandis not
propagate failure through a calculation using
==and!=the
Nada()value never equals itselfand never equals anything else
- nada_get(alt=Sentinel('Nada'))¶
Get an alternate value, defaults to
Nada().- Return type:
Any
- class pythonic_fp.fptools.singletons.NoValue¶
Singleton class representing a missing value.
similar to
Nonebutwhile
Nonerepresents “returned no values”NoValue()represents the absence of a value
usage
import NoValuefrompythonic-fp.fptools.singletonsand theneither use
NoValue()directlyor define
_noValue: Final[NoValue] = NoValue()don’t export it
compare using
isandis notnot
==or!=Nonemeans returned no values, soNone == Nonemakes senseif one or both values are missing, then what is there to compare?
- final class pythonic_fp.fptools.singletons.Sentinel(sentinel_name)¶
Singleton classes representing a sentinel values.
intended for library code, not to be exported/shared between modules
otherwise some of its intended typing guarantees may be lost
useful substitute for
Noneas a hidden sentinel valueallows
Noneto be stored in data structuresallows end users to choose to use
Noneor()as sentinel valuesalways equals itself (unlike
NoValue)
usage
import Sentinel and then either
define
_my_sentinel: Final[Sentinel] = Sentinel('my_sentinel')or use
Sentinel('my_sentinel')directly
compare using either
isandis notor==and!=the
Sentinel()value always equals itselfand never equals anything else, especially other sentinel values
State Monad¶
Pythonic FP - State Monad
Handling state functionally.
##### class State - Classic FP State Monad
A pure FP immutable implementation for the State Monad.
translated to Python from the book “Functional Programming in Scala”
authors Chiusana & Bjarnason
run “action” returns a tuple
(a, s)reversed to the typeState[S, A]the standard convention seen in the FP community
another “factoid” to remember
choose the name
bindinstead offlatmapthe
flatmapname is misleading for non-container-like monadsflatmapname too long,bindshorter to typewithout “do-notation”, code tends to march to the right
typing for the
modifyclass method may be a bit suspect
- class pythonic_fp.fptools.state.State(run)¶
Data structure generating values while propagating changes of state.
class
Staterepresents neither a state nor (value, state) pairit wraps a transformation old_state -> (value, new_state)
the
runmethod is this wrapped transformationbindis just state propagating function composition
- eval(init)¶
Evaluate the Monad via passing an initial state.
- Return type:
~A
- static get()¶
Set run action to return the current state
the current state is propagated unchanged
current value now set to current state
will need type annotation
- Return type:
State[ST,ST]
- static modify(f)¶
Modify previous state.
like put, but modify previous state via
fwill need type annotation
mypy has no “a priori” way to know what ST is
- Return type:
State[ST,tuple]
- static put(s)¶
Manually insert a state.
THe run action.
ignores previous state and swaps in a new state
assigns a canonically meaningless value to current value
- Return type:
State[ST,tuple]