grscheller.fp.function
Module fp.functional - compose and partially apply 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 function arguments return values:
- function swap: swap the arguments of a 2 argument function
- function sequenced: convert function to take a sequence of its arguments
- function partial: returns a partially applied function
1# Copyright 2024 Geoffrey R. Scheller 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""###Module fp.functional - compose and partially apply functions. 16 17Not a replacement for the std library's `functools` which is more about 18modifying function behavior through decorators than functional composition 19and application. 20 21#### FP utilities to manipulate function arguments return values: 22 23* function **swap:** swap the arguments of a 2 argument function 24* function **sequenced:** convert function to take a sequence of its arguments 25* function **partial:** returns a partially applied function 26 27""" 28from __future__ import annotations 29from collections.abc import Callable, Sequence 30from typing import Any 31 32__all__ = [ 'swap', 'sequenced', 'partial' ] 33 34## Functional Utilities 35 36def swap[U,V,R](f: Callable[[U,V],R]) -> Callable[[V,U],R]: 37 """Swap arguments of a two argument function.""" 38 return (lambda v,u: f(u,v)) 39 40def sequenced[R](f: Callable[..., R]) -> Callable[..., R]: 41 """Convert a function with arbitrary positional arguments to one taking 42 a sequence of the original arguments. 43 """ 44 def F(arguments: Sequence[Any]) -> R: 45 return f(*arguments) 46 return F 47 48def partial[R](f: Callable[..., R], *args: Any) -> Callable[..., R]: 49 """Partially apply arguments to a function, left to right. 50 51 * type-wise the only thing guaranteed is the return value 52 * best practice is to either 53 * use `partial` and `sequenced` results immediately and locally 54 * otherwise cast the results when they are created 55 56 """ 57 def apply(fn_seq: Callable[..., R], vals: Sequence[Any]) -> Callable[..., R]: 58 return (lambda restT: fn_seq(vals + restT)) 59 60 def wrap(*bs: Any) -> R: 61 return apply(sequenced(f), args)(bs) 62 63 return wrap
def
swap(f: 'Callable[[U, V], R]') -> 'Callable[[V, U], R]':
37def swap[U,V,R](f: Callable[[U,V],R]) -> Callable[[V,U],R]: 38 """Swap arguments of a two argument function.""" 39 return (lambda v,u: f(u,v))
Swap arguments of a two argument function.
def
sequenced(f: 'Callable[..., R]') -> 'Callable[..., R]':
41def sequenced[R](f: Callable[..., R]) -> Callable[..., R]: 42 """Convert a function with arbitrary positional arguments to one taking 43 a sequence of the original arguments. 44 """ 45 def F(arguments: Sequence[Any]) -> R: 46 return f(*arguments) 47 return F
Convert a function with arbitrary positional arguments to one taking a sequence of the original arguments.
def
partial(f: 'Callable[..., R]', *args: Any) -> 'Callable[..., R]':
49def partial[R](f: Callable[..., R], *args: Any) -> Callable[..., R]: 50 """Partially apply arguments to a function, left to right. 51 52 * type-wise the only thing guaranteed is the return value 53 * best practice is to either 54 * use `partial` and `sequenced` results immediately and locally 55 * otherwise cast the results when they are created 56 57 """ 58 def apply(fn_seq: Callable[..., R], vals: Sequence[Any]) -> Callable[..., R]: 59 return (lambda restT: fn_seq(vals + restT)) 60 61 def wrap(*bs: Any) -> R: 62 return apply(sequenced(f), args)(bs) 63 64 return wrap