Live data from Hacker News

Functional Python Programming

docs.python.org

71–80 of 105 posts

Re: Functional Python Programming

#71
post #21

shameless plug: I maintain a small library to do functional pipes. You can write: ( range(10) | Map(lambda x: x * 10) | Filter(lambda x: x % 2 == 0) | Reduce(lambda a, b: a + b) ) instead of: x = range(10) x = map(lambda x: x * 10, x) x = filter(lambda x: x % 2 == 0, x) x = reduce(lambda a, b: a + b, x) and more. https://tandav.github.io/pipe21/

Hmm, I've never seen code like the latter so it's unclear what problem this solves. I'd just write the pandas code snippet given below. Possibly with polars to make it lazy.

It is a fairly common pattern in low level stuff like hashing algorithms or cryptography.

Re: Functional Python Programming

#72

Earlier quoted context omitted.

Syntactically maybe, but I find it has a quite workable functional subset. Integers, floats, tuples, named tuples, and frozensets are all immutable, functions are values, etc. E.g.: https://joypy.osdn.io/notebooks/Derivatives_of_Regular_Expre... -or- https://github.com/calroc/xerblin/blob/master/xerblin/btree.... It's not fantastic, but it's not that bad.

I love both python and functional programming and I do write functional style python routinely. One barrier I hit is there is no immutable dict type. There is MappingProxyType but it's insufficient. I prefer using custom dataclass like objects built with pydantic and type checked with mypy. It's better than dict and can be made immutable but requires tons of boilerplate code which is a bit unpythonic.

> there is no immutable dict type

Does the namedtuple not suffice? Apologies if I'm being dense.

- - - -

It's not an immutable dict type (for one thing, this has linear lookup, the BTree would be better) but it's fun:

https://stackoverflow.com/questions/13708701/how-to-implemen...

In Python:

    from functools import partial

    def empty_dict(key):
        raise KeyError

    def _dict_add(dictionary, key, value, lookup):
        return value if key == lookup else dictionary(lookup)

    def dict_add(d, key, value):
        return partial(_dict_add, d, key, value)


    d = empty_dict
    d = dict_add(d, 'key0', 23)
    d = dict_add(d, 'key1', 18)

And then...

    >>> d('key0')
    23
    >>> d('key1')
    18
    >>> d('keyn')
    Traceback (most recent call last):
      File "", line 1, in 
        d('keyn')
      File "/usr/home/sforman/tmp_fn_dict.py", line 7, in _dict_add
        return value if key == lookup else dictionary(lookup)
      File "/usr/home/sforman/tmp_fn_dict.py", line 7, in _dict_add
        return value if key == lookup else dictionary(lookup)
      File "/usr/home/sforman/tmp_fn_dict.py", line 4, in empty_dict
        raise KeyError
    KeyError


- - - -

> objects built with pydantic and type checked with mypy

FWIW, after ~15 years of professional Python development, I'm not into types [in Python]. My attitude is that if you really need them you should switch to e.g. OCaml or something that does them right. I love Python but I wouldn't use it anymore for anything other than scripts and maybe prototyping.

Re: Functional Python Programming

#73
post #21

shameless plug: I maintain a small library to do functional pipes. You can write: ( range(10) | Map(lambda x: x * 10) | Filter(lambda x: x % 2 == 0) | Reduce(lambda a, b: a + b) ) instead of: x = range(10) x = map(lambda x: x * 10, x) x = filter(lambda x: x % 2 == 0, x) x = reduce(lambda a, b: a + b, x) and more. https://tandav.github.io/pipe21/

Hmm, I've never seen code like the latter so it's unclear what problem this solves. I'd just write the pandas code snippet given below. Possibly with polars to make it lazy.

My library solves 2 problems.

1. It does not require to wrap your iterable into some wrapper to use functional methods. It takes an iterable/object and returns another iterable/object. You don't have to unwrap it after transformations.

2. it uses oneliners (library is 80LOC single file) for most of the methods. You can just copy-paste it to use instead of install and import. E.g map, filter, reduce is just:

    class B:
        def __init__(self, f): self.f = f
    class Pipe  (B): __ror__ = lambda self, x: self.f(x)
    class Map   (B): __ror__ = lambda self, x: map   (self.f, x)
    class Filter(B): __ror__ = lambda self, x: filter(self.f, x)
    class Reduce(B): __ror__ = lambda self, it: functools.reduce(self.f, it, *self.args)

Re: Functional Python Programming

#74

What's so useful about iterators and generators. The article says how to use them but not why. If you already know how to make list comprehensions and use "for elt in coll", do they let you do anything new?

You could probably find more uses, but here are some:

Generators allow one to easily transform code from non-buffered to buffered. Consider this example:

    for y in x:
        do(z)
Now, x may be a collection that was eagerly evaluated in previous steps, let's say a list, but then you've discovered that this list is too big to fit in memory, and you want to generate it in manageable fixed-size chunks, so you replace the code that created x to make x a generator. You don't have to touch the code above -- it will work the same way because generators have the same interface as collections.

Another use: generators are used to implement async / await. I personally find this idea ridiculously stupid, but a lot of people (and especially those who don't understand what it does) like it a lot. Yielding mechanism, which is a feature of generators, is the one that's used to communicate / switch between co-routines (tasks) of asyncio.

Another aspect, besides bufferization is that you might want to delegate control over how much looping you want to do to a separate chunk of code. I.e. you may want to separate the generation of elements (hence, generator) from eg. filtering them, or transforming them in some way, or reducing them etc. If you didn't have this ability, you'd have to generate the entire collection upfront, and if your computation takes multiple steps that you'd like to separate into different code chunks, you'd have to also generate intermediate collections, even though, potentially, you don't need some of the elements in those collections. Consider, for example:

    def powers(start, end, power):
        return (x ** power for x in range(start, end))

    def flt(a, b, c):
        return a + b == c

    def disprove_flt(upto, upto_power):
        for power in range(upto_power):
            for a, b, c in zip(powers(1, upto - 2, power), powers(2, upto - 1, power), powers(3, upto, power)):
                if flt(a, b, c):
                    return a, b, c
        return None, None, None
Which is, of course not a correct way to search for the counterexample to Fermat's last theorem, but I tried to find a popular enough subject so that the example was easier to follow.

An exercise to the reader: rewrite the code above in such a way as to eliminate "upto" and "upto_power", i.e. to search until a counterexample is found (or indefinitely).

Re: Functional Python Programming

#75
post #48

Iterators are a fundamentally non-functional construct. Long ago Henry Baker wrote "Signs of Weakness in Object-Oriented Languages": https://plover.com/~mjd/misc/hbaker-archive/Iterator.html

You just broke my brain. I will read it though, just give me 6 months!

Re: Functional Python Programming

#76
post #8

Sadly, Python is a pretty poor functional language. The core of functional programming is about avoiding mutable states , not much about anonymous functions or passing functions as data. To do proper functional programming in Python, there should be IMO: - a way to enforce non-mutable variables/objects; - non-mutable collections; - proper support for recursion and tail-recursion optimization; - a better syntax for an…

Not for Lisp or some ML derived languages, FP isn't whatever Haskell does.

Re: Functional Python Programming

#77

Fun to work on doing FP in languages that don't really support it, but in my view a language has to be built for FP for it to be a practical option in any real applications. Several obvious reasons for Python being a poor lang in which to do FP: - mutable data structures - no built-in function composition - limited support for HOF - no tail call optimization (AFAIK) - performance in general isn't great and I imagine…

Scheme is one of the few FP languages with required TCO on the language standard, and alongside Lisp, Caml Light, Standard ML, OCaml, F#, Scala, supports mutable data structures.

Re: Functional Python Programming

#78
Worthless functionality in a worthless language. Only topped by the bizarre and unverified claims in its documentation:

    Most programming languages are procedural:
Did the author count? -- The answer is a clear and resounding "No". The author pulled this factoid out if his rear end. Maybe. Maybe not. It's not even clear by what's meant by "all languages" -- all possible languages? all languages known to author? all languages used in Github? And why should anyone care about this kind of multitude?

    Lisp
Again, people who've never seen any Lisp, or vaguely remember their college days when some course requested from them to write a function to figure out if a string is a palindrome in Scheme think that Lisp is a single language. The author just decided to demonstrate his blistering ignorance by including something that he thought would render him as more experienced than their readers.

Later the author perpetuates all sorts of absurd myths about "functional programming" s.a. increased modularity or ease of debugging. Apparently, author had never used step-debugger nor had he wrote anything in any popular programming language that advertises itself as functional to experience first-hand this "ease" he's talking about. Needless to say that nothing in functional programming prevents programmers from writing long functions... In practice, however, some languages which advertise themselves as "functional" have pathologically bad / hard to read syntax (eg. Haskell), and functions longer than some 10 lines or so become too difficult to understand even to people who believe themselves to be proficient in those languages.

Author is simply lying when he claims that generators are a kind of function, which can be simply verified:

    >>> def generate_ints(n):
    ...   for i in range(n):
    ...     yield i
    ... 
    >>> type(generate_ints(1))
    
    >>> type(generate_ints(1)).mro()
    [, ]
    >>> isinstance(generate_ints(1), type(generate_ints))
    False
Generators and functions are unrelated. Neither is a kind of other.

On top of that, author frequently violates Python's coding conventions (eg. capital letters in variable names, assigning lambdas to variables).

---

But, bottom line: crappy language deserves no better documentation than this.

Re: Functional Python Programming

#79
post #54

Earlier quoted context omitted.

Readability isn't the best. Also what you present here is method chaining and not functional pipes.

What difference does it make? They're conceptually the same thing. You're mapping immutable data to map/reduce/filter like pure functions to get new data.

Improved code readability. The pipe operator was a game changer for me even after 20 years of programming (I know I was probably touching wrong things).

Re: Functional Python Programming

#80

Worthless functionality in a worthless language. Only topped by the bizarre and unverified claims in its documentation: Most programming languages are procedural: Did the author count? -- The answer is a clear and resounding "No". The author pulled this factoid out if his rear end. Maybe. Maybe not. It's not even clear by what's meant by "all languages" -- all possible languages? all languages known to author? all la…

Have a beer, bro
Post reply on HN