Git Product home page Git Product logo

fn.py's Introduction

Fn.py: enjoy FP in Python

Despite the fact that Python is not pure-functional programming language, it's multi-paradigm PL and it gives you enough freedom to take credits from functional programming approach. There are theoretical and practical advantages to the functional style:

  • Formal provability
  • Modularity
  • Composability
  • Ease of debugging and testing

Fn.py library provides you with missing "batteries" to get maximum from functional approach even in mostly-imperative program.

More about functional approach from my Pycon UA 2012 talks: Functional Programming with Python.

Scala-style lambdas definition

from fn import _
from fn.op import zipwith
from itertools import repeat

assert list(map(_ * 2, range(5))) == [0,2,4,6,8]
assert list(filter(_ < 10, [9,10,11])) == [9]
assert list(zipwith(_ + _)([0,1,2], repeat(10))) == [10,11,12]

More examples of using _ you can find in test cases declaration (attributes resolving, method calling, slicing).

Attention! If you work in interactive python shell, your should remember that _ means "latest output" and you'll get unpredictable results. In this case, you can do something like from fn import _ as X (and then write functions like X * 2).

If you are not sure, what your function is going to do, you can print it:

from fn import _

print (_ + 2) # "(x1) => (x1 + 2)"
print (_ + _ * _) # "(x1, x2, x3) => (x1 + (x2 * x3))"

_ will fail with ArityError (TypeError subclass) on inaccurate number of passed arguments. This is one more restrictions to ensure that you did everything right:

>>> from fn import _
>>> (_ + _)(1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fn/underscore.py", line 82, in __call__
    raise ArityError(self, self._arity, len(args))
fn.underscore.ArityError: (_ + _) expected 2 arguments, got 1

Persistent data structures

Attention: Persistent data structures are under active development.

Persistent data structure is a data structure that always preserves the previous version of itself when it is modified (more formal information on Wikipedia). Each operation with such data structure yields a new updated structure instead of in-place modification (all previous versions are potentially available or GC-ed when possible).

Lets take a quick look:

>>> from fn.immutable import SkewHeap
>>> s1 = SkewHeap(10)
>>> s2 = s1.insert(20)
>>> s2
<fn.immutable.heap.SkewHeap object at 0x10b14c050>
>>> s3 = s2.insert(30)
>>> s3
<fn.immutable.heap.SkewHeap object at 0x10b14c158> # <-- other object
>>> s3.extract()
(10, <fn.immutable.heap.SkewHeap object at 0x10b14c050>)
>>> s3.extract() # <-- s3 isn't changed
(10, <fn.immutable.heap.SkewHeap object at 0x10b11c052>)

If you think I'm totally crazy and it will work despairingly slow, just give it 5 minutes. Relax, take a deep breath and read about few techniques that make persistent data structures fast and efficient: structural sharing and path copying.

To see how it works in "pictures", you can check great slides from Zach Allaun's talk (StrangeLoop 2013): "Functional Vectors, Maps And Sets In Julia".

And, if you are brave enough, go and read:

  • Chris Okasaki, "Purely Functional Data Structures" (Amazon)
  • Fethi Rabhi and Guy Lapalme, "Algorithms: A Functional Programming Approach" (Amazon)

Available immutable data structures in fn.immutable module:

  • LinkedList: most "obvious" persistent data structure, used as building block for other list-based structures (stack, queue)
  • Stack: wraps linked list implementation with well-known pop/push API
  • Queue: uses two linked lists and lazy copy to provide O(1) enqueue and dequeue operations
  • Deque (in progress): "Confluently Persistent Deques via Data Structural Bootstrapping"
  • Deque based on FingerTree data structure (see more information below)
  • Vector: O(log32(n)) access to elements by index (which is near-O(1) for reasonable vector size), implementation is based on BitmappedTrie, almost drop-in replacement for built-in Python list
  • SkewHeap: self-adjusting heap implemented as a binary tree with specific branching model, uses heap merge as basic operation, more information - "Self-adjusting heaps"
  • PairingHeap: "The Pairing-Heap: A New Form of Self-Adjusting Heap"
  • Dict (in progress): persistent hash map implementation based on BitmappedTrie
  • FingerTree (in progress): "Finger Trees: A Simple General-purpose Data Structure"

Use appropriate doc strings to get more information about each data structure as well as sample code.

To get more clear vision of how persistent heaps work (SkewHeap and PairingHeap), you can look at slides from my talk "Union-based heaps" (with analyzed data structures definitions in Python and Haskell).

Note. Most functional languages use persistent data structures as basic building blocks, well-known examples are Clojure, Haskell and Scala. Clojure community puts much effort to popularize programming based on the idea of data immutability. There are few amazing talk given by Rich Hickey (creator of Clojure), you can check them to find answers on both questions "How?" and "Why?":

Streams and infinite sequences declaration

Lazy-evaluated Scala-style streams. Basic idea: evaluate each new element "on demand" and share calculated elements between all created iterators. Stream object supports << operator that means pushing new elements when it's necessary.

Simplest cases:

from fn import Stream

s = Stream() << [1,2,3,4,5]
assert list(s) == [1,2,3,4,5]
assert s[1] == 2
assert list(s[0:2]) == [1,2]

s = Stream() << range(6) << [6,7]
assert list(s) == [0,1,2,3,4,5,6,7]

def gen():
    yield 1
    yield 2
    yield 3

s = Stream() << gen << (4,5)
assert list(s) == [1,2,3,4,5]

Lazy-evaluated stream is useful for infinite sequences, i.e. fibonacci sequence can be calculated as:

from fn import Stream
from fn.iters import take, drop, map
from operator import add

f = Stream()
fib = f << [0, 1] << map(add, f, drop(1, f))

assert list(take(10, fib)) == [0,1,1,2,3,5,8,13,21,34]
assert fib[20] == 6765
assert list(fib[30:35]) == [832040,1346269,2178309,3524578,5702887]

Trampolines decorator

fn.recur.tco is a workaround for dealing with TCO without heavy stack utilization. Let's start from simple example of recursive factorial calculation:

def fact(n):
    if n == 0: return 1
    return n * fact(n-1)

This variant works, but it's really ugly. Why? It will utilize memory too heavy cause of recursive storing all previous values to calculate final result. If you will execute this function with big n (more than sys.getrecursionlimit()) CPython will fail with

>>> import sys
>>> fact(sys.getrecursionlimit() * 2)
... many many lines of stacktrace ...
RuntimeError: maximum recursion depth exceeded

Which is good, cause it prevents you from terrible mistakes in your code.

How can we optimize this solution? Answer is simple, lets transform function to use tail call:

def fact(n, acc=1):
    if n == 0: return acc
    return fact(n-1, acc*n)

Why this variant is better? Cause you don't need to remember previous values to calculate final result. More about tail call optimization on Wikipedia. But... Python interpreter will execute this function the same way as previous one, so you won't win anything.

fn.recur.tco gives you mechanism to write "optimized a bit" tail call recursion (using "trampoline" approach):

from fn import recur

@recur.tco
def fact(n, acc=1):
    if n == 0: return False, acc
    return True, (n-1, acc*n)

@recur.tco is a decorator that execute your function in while loop and check output:

  • (False, result) means that we finished
  • (True, args, kwargs) means that we need to call function again with other arguments
  • (func, args, kwargs) to switch function to be executed inside while loop

The last variant is really useful, when you need to switch callable inside evaluation loop. Good example for such situation is recursive detection if given number is odd or even:

>>> from fn import recur
>>> @recur.tco
... def even(x):
...     if x == 0: return False, True
...     return odd, (x-1,)
...
>>> @recur.tco
... def odd(x):
...     if x == 0: return False, False
...     return even, (x-1,)
...
>>> print even(100000)
True

Attention: be careful with mutable/immutable data structures processing.

Itertools recipes

fn.uniform provides you with "unification" of lazy functionality for few functions to work the same way in Python 2+/3+:

  • map (returns itertools.imap in Python 2+)
  • filter (returns itertools.ifilter in Python 2+)
  • reduce (returns functools.reduce in Python 3+)
  • zip (returns itertools.izip in Python 2+)
  • range (returns xrange in Python 2+)
  • filterfalse (returns itertools.ifilterfalse in Python 2+)
  • zip_longest (returns itertools.izip_longest in Python 2+)
  • accumulate (backported to Python < 3.3)

fn.iters is high-level recipes to work with iterators. Most of them taken from Python docs and adopted to work both with Python 2+/3+. Such recipes as drop, takelast, droplast, splitat, splitby I have already submitted as docs patch which is review status just now.

  • take, drop
  • takelast, droplast
  • head (alias: first), tail (alias: rest)
  • second, ffirst
  • compact, reject
  • every, some
  • iterate
  • consume
  • nth
  • padnone, ncycles
  • repeatfunc
  • grouper, powerset, pairwise
  • roundrobin
  • partition, splitat, splitby
  • flatten
  • iter_except
  • first_true

More information about use cases you can find in docstrings for each function in source code and in test cases.

High-level operations with functions

fn.F is a useful function wrapper to provide easy-to-use partial application and functions composition.

from fn import F, _
from operator import add, mul

# F(f, *args) means partial application
# same as functools.partial but returns fn.F instance
assert F(add, 1)(10) == 11

# F << F means functions composition,
# so (F(f) << g)(x) == f(g(x))
f = F(add, 1) << F(mul, 100)
assert list(map(f, [0, 1, 2])) == [1, 101, 201]
assert list(map(F() << str << (_ ** 2) << (_ + 1), range(3))) == ["1", "4", "9"]

It also give you move readable in many cases "pipe" notation to deal with functions composition:

from fn import F, _
from fn.iters import filter, range

func = F() >> (filter, _ < 6) >> sum
assert func(range(10)) == 15

You can find more examples for compositions usage in fn._ implementation source code.

fn.op.apply executes given function with given positional arguments in list (or any other iterable). fn.op.flip returns you function that will reverse arguments order before apply.

from fn.op import apply, flip
from operator import add, sub

assert apply(add, [1, 2]) == 3
assert flip(sub)(20,10) == -10
assert list(map(apply, [add, mul], [(1,2), (10,20)])) == [3, 200]

fn.op.foldl and fn.op.foldr are folding operators. Each accepts function with arity 2 and returns function that can be used to reduce iterable to scalar: from left-to-right and from right-to-left in case of foldl and foldr respectively.

from fn import op, _

folder = op.foldr(_ * _, 1)
assert 6 == op.foldl(_ + _)([1,2,3])
assert 6 == folder([1,2,3])

Use case specific for right-side folding is:

from fn.op import foldr, call

assert 100 == foldr(call, 0 )([lambda s: s**2, lambda k: k+10])
assert 400 == foldr(call, 10)([lambda s: s**2, lambda k: k+10])

Function currying

fn.func.curried is a decorator for building curried functions, for example:

>>> from fn.func import curried
>>> @curried
... def sum5(a, b, c, d, e):
...     return a + b + c + d + e
...
>>> sum5(1)(2)(3)(4)(5)
15
>>> sum5(1, 2, 3)(4, 5)
15

Functional style for error-handling

fn.monad.Option represents optional values, each instance of Option can be either instance of Full or Empty. It provides you with simple way to write long computation sequences and get rid of many if/else blocks. See usage examples below.

Assume that you have Request class that gives you parameter value by its name. To get uppercase notation for non-empty striped value:

class Request(dict):
    def parameter(self, name):
        return self.get(name, None)

r = Request(testing="Fixed", empty="   ")
param = r.parameter("testing")
if param is None:
    fixed = ""
else:
    param = param.strip()
    if len(param) == 0:
        fixed = ""
    else:
        fixed = param.upper()

Hmm, looks ugly.. Update code with fn.monad.Option:

from operator import methodcaller
from fn.monad import optionable

class Request(dict):
    @optionable
    def parameter(self, name):
        return self.get(name, None)

r = Request(testing="Fixed", empty="   ")
fixed = r.parameter("testing")
         .map(methodcaller("strip"))
         .filter(len)
         .map(methodcaller("upper"))
         .get_or("")

fn.monad.Option.or_call is good method for trying several variant to end computation. I.e. use have Request class with optional attributes type, mimetype, url. You need to evaluate "request type" using at least one attribute:

from fn.monad import Option

request = dict(url="face.png", mimetype="PNG")
tp = Option \
        .from_value(request.get("type", None)) \ # check "type" key first
        .or_call(from_mimetype, request) \ # or.. check "mimetype" key
        .or_call(from_extension, request) \ # or... get "url" and check extension
        .get_or("application/undefined")

Installation

To install fn.py, simply:

$ pip install fn

Or, if you absolutely must:

$ easy_install fn

You can also build library from source

$ git clone https://github.com/kachayev/fn.py.git
$ cd fn.py
$ python setup.py install

Work in progress

"Roadmap":

  • fn.monad.Either to deal with error logging
  • C-accelerator for most modules

Ideas to think about:

  • Scala-style for-yield loop to simplify long map/filter blocks

Contribute

  1. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
  2. Fork the repository on Github to start making your changes to the master branch (or branch off of it).
  3. Write a test which shows that the bug was fixed or that the feature works as expected.

How to find me

  • Twitter: @kachayev
  • Email: kachayev <at> gmail.com

fn.py's People

Contributors

adamsar avatar aitjcize avatar alphaho avatar calebsmith avatar ccfontes avatar danielhfrank avatar digenis avatar gabrielpjordao avatar kachayev avatar kangol avatar mjkaye avatar pawroman avatar sbinq avatar w-m avatar z4y4ts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fn.py's Issues

There are wrong comments here

class F(object):
    def __rshift__(self, g):
        """Overload << operator for F instances"""
        return self.__class__.__compose(self.__ensure_callable(g), self.f)

    def __lshift__(self, g):
        """Overload >> operator for F instances"""
        return self.__class__.__compose(self.f, self.__ensure_callable(g))

the operator >> is right shift and << is left shift respectively

Required `memoise` function

I haven't found memoise function implementation. I suggest to have at least two implementations with weakref and regular dictionary.

getattr operator fails with default value in _

Easy to reproduce using fn.op.foldr (cause it's initially flip given function in order to work with c-level reducing).

>>> from fn import _ as XXX
>>> from fn.op import flip, foldr
>>>
>>> flip(XXX + XXX)
getattr((_ + _), __flipback__)
>>>
>>> foldr(XXX + XXX)([1,2,3])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fn/op.py", line 70, in fold
    return iters.reduce(*args)
  File "fn/underscore.py", line 85, in __call__
    return self._callback(*args)
  File "fn/func.py", line 51, in __call__
    return self.f(*args, **kwargs)
  File "fn/func.py", line 32, in <lambda>
    return cls(lambda *args, **kwargs: f(g(*args, **kwargs)))
AttributeError: 'int' object has no attribute '__flipback__'

Method calling from _ not working?

Hi,

I'm not sure whether this is supposed to work:

from fn import _ as X
X.endswith('tif')('image.tif')

This prints "False".

Looking at the tests I see this form:

from fn import _ as X
X.call('endswith', '.tif')('image.tif')

This correctly evaluates to True, but is significantly uglier. What's happening in the first form?

API Documentation

Hi, interesting library!

Do you have a function to use-case reference?
Something similar to https://ramdajs.com/docs

Coming back to Python from javascript and it's a difficulty curve for new users if they have to dig through the source to find use cases.

If you don't have one, perhaps we could setup a Github action to generate the docs using pydoc or other, whenever a merge to master happens?

(question) Folds implementation and reduce()

I want to add foldl and foldr tomorrow, but I feel like this needs some discussion. CPython's builtin reduce() is a left-fold. Can/should we rely on this or should it be replaced with a custom left-fold implementation? reduce() should be faster, but the actual result of it is up to the implementation if I understand correctly.

Currying

I tried this code for function currying

from fn.func import curried
@curried
def sum5(a, b, c, d, e):
    return a + b + c + d + e

sum5(1)(2)(3)(4)(5)
15
sum5(1, 2, 3)(4, 5)
15

but when I call the function I get the following error

Traceback (most recent call last):
File "", line 1, in
File "fn/func.py", line 83, in _curried
return curried(partial(func, _args, *_kwargs))
File "fn/func.py", line 69, in curried
@wraps(func)
File "/usr/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'functools.partial' object has no attribute 'module'

@composable decorator

Special @composable decorator to bring new functionality to function:

  • easy way to compose functions (using one of standard operators)
  • overload apply operator

Test fails for PyPy (FingerTreeDequeTestCase)

Currently, the test fails for PyPy as shown below.

======================================================================

FAIL: test_iterator (__main__.FingerTreeDequeTestCase)

----------------------------------------------------------------------

Traceback (most recent call last):

File "tests.py", line 1083, in test_iterator

self.assertEqual([], list(Deque()))

AssertionError: Lists differ: [] != [<function <lambda> at 0x00007...

Second list contains 1 additional elements.

First extra element 0:

<function <lambda> at 0x00007fe2f940b9e8>

- []

+ [<function <lambda> at 0x00007fe2f940b9e8>]

----------------------------------------------------------------------

Ran 123 tests in 1.195s

FAILED (failures=1)

curried decorator does not work with type annotations

from fn.func import curried
@curried
def sum5(a, b, c, d, e):
    return a + b + c + d +e
sum5(1)(2)(3)(4)(5)
Out[6]: 15
@curried
def sum5(a: int, b: int, c: int, d: int, e: int):
    return a + b + c + d +e
sum5(1)(2)(3)(4)(5)
Traceback (most recent call last):
  File "/home/sphelps/mambaforge/envs/symbiotica-fund/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3505, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-8-e80b0556d52b>", line 1, in <module>
    sum5(1)(2)(3)(4)(5)
  File "/home/sphelps/mambaforge/envs/symbiotica-fund/lib/python3.10/site-packages/fn/func.py", line 78, in _curried
    spec = getargspec(f)
  File "/home/sphelps/mambaforge/envs/symbiotica-fund/lib/python3.10/inspect.py", line 1237, in getargspec
    raise ValueError("Function has keyword-only parameters or annotations"
ValueError: Function has keyword-only parameters or annotations, use inspect.signature() API which can support them

PEP-8?

As per http://legacy.python.org/dev/peps/pep-0008. Just to make the codebase look more organised and consistent.

I'm not sure how mature packages like 'autopep8' are at present, but worth having a look if we are going with this. There is no harm doing it manually though. Optionally, we can also add 'flake8' testing to CI for future commits.

What are your thoughts on this, @kachayev?

how to use _ in a function

I've try :
print math.sin(_)
it turn out to be wrong, but it works for:
lambda x: math.sin(x)

Does _ always be same as lambda caluse?
any limitation?
thx ~~

about rock tail recursion


@tail_call_optimized
def even(x):
if x == 0: return True
return odd(x-1)

@tail_call_optimized
def odd(x):
if x == 0: return False
return even(x-1)

print even(100000)

can recur.tco resolve rock tail recursion?


Option class should have all methods of Full and Empty

Otherwise pylint complains (fairly) that Option does not have, for example, "map".

Just add these lines at monad.py:72

    def map(self, callback):
        raise NotImplementedError()

    def filter(self, callback):
        raise NotImplementedError()

    def get_or(self, default):
        raise NotImplementedError()

    def get_or_call(self, callback, *args, **kwargs):
        raise NotImplementedError()

    def or_else(self, default):
        raise NotImplementedError()

    def or_call(self, callback, *args, **kwargs):
        raise NotImplementedError()

This is a pretty minor issue and somewhat stylistic, so feel free to ignore :) Thanks for the nice library!

Strange _ behavior

Hi!
I ran your example and got this:

In [1]: from fn.iters import map
In [2]: from fn import _
In [3]: list(map(_ * 2, range(5)))
Out[3]: [0, 2, 4, 6, 8]
In [4]: list(map(_ * 2, range(5)))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-448bfa47eeda> in <module>()
----> 1 list(map(_ * 2, range(5)))
TypeError: 'list' object is not callable

python 2.7.3 windows 7 x64

iters.flatten attempts to flatten dict

Hi, It's quite misleading that flatten does not ignore dictionaries.

For instance, the following snippet is not expected for most flatten arounds.

In: list(iters.flatten([{'a':1}]))
Out: ['a']

It seems more useful to have it return [{'a': 1}] in this case.

Add LinkedList.from_iterable

Similar to FingerTree.from_iterable.

I currently have an iterable_to_linkedlist function, but it would be easier if that feature was part of LinkedList.

def iterable_to_linkedlist(it):
    ''' iterable -> LinkedList

    produces LinkedList with the contents of the consumed iterable
    '''
    return foldr(lambda x, y: y.cons(x), LinkedList())(tuple(it))

foldr so it preserves order, tuple(it) in case "it" is an iterator (otherwise the reversed() in foldr complains).

Suggestions for improved implementation? Otherwise I'm happy to make a pull-request.

ImportError: cannot import name Iterable from collections on Python 3.10

It just doesn't work. It expects Iterable from collections but this is not available.

This log is from building python310Packages.fn with nix but should be reproducible on any python 3.10 out there

Sourcing python-remove-tests-dir-hook
Sourcing python-catch-conflicts-hook.sh
Sourcing python-remove-bin-bytecode-hook.sh
Sourcing setuptools-build-hook
Using setuptoolsBuildPhase
Using setuptoolsShellHook
Sourcing pip-install-hook
Using pipInstallPhase
Sourcing python-imports-check-hook.sh
Using pythonImportsCheckPhase
Sourcing python-namespaces-hook
Sourcing setuptools-check-hook
Using setuptoolsCheckPhase
@nix { "action": "setPhase", "phase": "unpackPhase" }
unpacking sources
unpacking source archive /nix/store/bhfhqwy9y6x63h4dq2lpimzkl0b2b29i-fn-0.4.3.tar.gz
source root is fn-0.4.3
setting SOURCE_DATE_EPOCH to timestamp 1407495729 of file fn-0.4.3/setup.cfg
@nix { "action": "setPhase", "phase": "patchPhase" }
patching sources
@nix { "action": "setPhase", "phase": "configurePhase" }
configuring
no configure script, doing nothing
@nix { "action": "setPhase", "phase": "buildPhase" }
building
Executing setuptoolsBuildPhase
Traceback (most recent call last):
  File "/build/fn-0.4.3/nix_run_setup", line 8, in <module>
    exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\\r\\n', '\\n'), __file__, 'exec'))
  File "setup.py", line 6, in <module>
    import fn
  File "/build/fn-0.4.3/fn/__init__.py", line 1, in <module>
    from .stream import Stream
  File "/build/fn-0.4.3/fn/stream.py", line 9, in <module>
    from .iters import map, range
  File "/build/fn-0.4.3/fn/iters.py", line 2, in <module>
    from collections import deque, Iterable
ImportError: cannot import name 'Iterable' from 'collections' (/nix/store/443qk9752l44kfp8pzsrp0m9jyq6jz2p-python3-3.10.3/lib/python3.10/collections/__init__.py)

Backlinks: NixOS/nixpkgs#167402

Test fails for Python 3.x ('reduce' not defined)

Currently, the test fails for Python 3.x due to reduce not being defined.

======================================================================
ERROR: test_iterator (__main__.FingerTreeDequeTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests.py", line 1086, in test_iterator
    self.assertEqual(sum(range(1,20)), sum(Deque.from_iterable(range(1,20))))
  File "/home/microamp/devel/projs/fn.py/fn/immutable/finger.py", line 138, in from_iterable
    return FingerTree.from_iterable(lambda x: x, it)
  File "/home/microamp/devel/projs/fn.py/fn/immutable/finger.py", line 122, in from_iterable
    return reduce(lambda acc, curr: acc.push_front(curr), it, tree)
NameError: global name 'reduce' is not defined

----------------------------------------------------------------------
Ran 123 tests in 0.110s

FAILED (errors=1)

underscore lambda cannot use __contains__ (IOW "in") [renamed issue]

I often find myself in the situation where I want to create a filter and, if using the underscore syntax, _ is a sequence. For instance:

    my_filter = F() >> (filter, _[1] in TYPE_MAP)

In this situation, _ will be an item from a dict.items() and will, therefore, be a 2-tuple. I want my_filter to return only the items for which the value (the second item in the 2-tuple) is in TYPE_MAP.

However, when run, the above code results in TypeError: "'bool' object is not callable". It looks as if it's trying to evaluate _[1] in TYPE_MAP at the time of partial function creation, rather than when actually calling the resulting function.

Is there a way to achieve this with the underscore syntax, or do I just have to use the lambda syntax?

Thanks

Active Fork?

Has anyone considered starting an active fork of this repo? I see it has been forked a number of times, but no forks are being actively maintained.

I don't feel qualified to maintain a project like this, but it is such a great library I don't want it to die! If no one else steps up within a month I'll create a fork and pledge to provide active updates. The fork will have to be deployed on PyPi as well.

make fn._ pickle

I want use fn._ in a spark program. e.g.

 num = rdd.filter('00:00:00' <= _[0] <= '12:00:00')

but I got serialize error, can any one give me some advice to solve this problem.

Function Composition

Often, when I chain functions, I find myself using the following pattern:

(F() >> foo >> bar >> baz)(x)

If this were clojure, I could write:

(-> x foo bar baz)

Notice the input on the left. Is there any to way to do this in python/fn?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.