9 ms·
> Python is a strongly typed language In some sense, sure. But it's not statically typed, which is (in my opinion) much more important. Reasoning about functio
by DonaldPShimoda 7y ago
> Python is a strongly typed language
In some sense, sure. But it's not statically typed, which is (in my opinion) much more important. Reasoning about function composition in Haskell is straightforward because you just have to make the types line up correctly. Technically this is true in Python, but you don't have any mechanism (by default) to check whether your implementation is correct other than writing enough tests. Yes, you can use type hints and an external type checker, but that's still not a great solution compared to any statically styped language.
> you can write purely functionally in it all you want
Except that Python does not optimize tail calls, which means that implementing a simple recursive solution to a problem requires reasoning about the stack (which it shouldn't, if we're doing things functionally). There are hacks to optimize for some tail calls via decorators and explicitly checking the stack, but this decreases runtime performance and does not work for things like mutual tail call recursion (which is optimized in functional-first languages). To me, lack of TCO is a huge dealbreaker. I mean I still write my Python code semi-functionally because I find FP easier to reason about, but it's more work than doing it in Haskell.
And don't even get me started on the lack of support for features like algebraic data types, pattern matching, etc.
---
I love Python; it's my most-used language by far, I think. And I love functional programming. And I even try to write my Python code in a functional style! But Python is not what I would call a "good" language for functional programming.
- j88439h84 7y ago> Yes, you can use type hints and an external type checker, but that's still not a great solution compared to any statically styped language. Why not?
- DonaldPShimoda 7y agoWell it isn't built-in, so that's an obstacle to its use right there. You also have to rely on two sets of developers to ensure you can stay up-to-date, and this introduces more potential for errors and bugs. Furthermore, the static type systems supported by, e.g., MyPy do not perfectly reflect the dynamic semantics of the Python language. So what you really have is some subset of Python that you're allowed to use fully. (This is probably not a big issue for many people, to be fair.) Another problem is that systems like MyPy require you to actually write the type hints in a lot of places. Manifest type systems are not favored because requiring users to write more is almost never something they're happy with. One of the most common complaints about Java is that you have to write so many types everywhere, and while you don't need that many in statically typing your Python code, you do need quite a few. A statically typed language with good type inference definitely wins here. Type hints are also not very expressive because they're purely nominal, but Python's type system is actually structural (which is true for most dynamically typed languages, as far as I know). I haven't experimented with this too much because I don't use a type checker like MyPy, but I seem to remember that it treated types nominally. (I think I've also seen some indication that they're working to extend type hints to support structural types, but last I checked it wasn't right around the corner or anything.) What I'm getting at here is that while external type checkers like MyPy can be considered an improvement over the dynamically typed language itself, they come with a lot of caveats that make me uncomfortable. There are few cases where I truly prefer dynamic types to static ones.
- j88439h84 7y agoThanks for elaborating, that's all interesting. > Well it isn't built-in, so that's an obstacle to its use right there. Yeah, no doubt. > You also have to rely on two sets of developers to ensure you can stay up-to-date, and this introduces more potential for errors and bugs. [Edit: Some of] The maintainers of mypy are python core developers, so it's not quite two sets of developers. :-) > Another problem is that systems like MyPy require you to actually write the type hints in a lot of places. Manifest type systems are not favored because requiring users to write more is almost never something they're happy with. One of the benefits of optional typing is we can automatically add type annotations from runtime observation. Then we get the documentation benefits of typing without the work of writing them. https://monkeytype.readthedocs.io https://monkeytype.readthedocs.io is a good tool for this. > (I think I've also seen some indication that they're working to extend type hints to support structural types, but last I checked it wasn't right around the corner or anything.) Mypy supports structural subtyping currently. It's extremely useful. https://mypy.readthedocs.io/en/latest/protocols.html https://mypy.readthedocs.io/en/latest/protocols.html
- DonaldPShimoda 7y ago> Yeah, no doubt. (re: two tools) Honestly I think this is my main issue with it. I have the same feelings about Liquid Haskell having a separate executable to check the liquid types. It's not the worst... but not ideal. > The maintainers of mypy are python core developers, so it's not quite two sets of developers. :-) Shows you what I know haha. Still, there's no guarantee that it'll always be the same developers. And even then, it's two separate development cycles. MyPy could fall behind someday or something. Just something to think about! > One of the benefits of optional typing is we can automatically add type annotations from runtime observation. This has a high coolness factor but is not very sound, because it'll require writing good tests that allow you to make those observations in the first place, right? This is adding to the total test-writing workload of the user, instead of a statically typed language where you just get all of this for "free". > Mypy supports structural subtyping currently. I didn't realize this! That's good! Clearly I need to do more research before commenting haha. This does appear to require an additional external dependency though (the `typing_extensions` module), which is too bad.
- lazyguy2 7y ago> Except that Python does not optimize tail calls Tail call reversion optimization just exists so people can right 'for loops' in a manner that looks 'functional' on the surface. Because that is what tail optimization does.. it turns your recursive function into a loop. So every time you write a for loop in Python just tell yourself that it's actually just a brilliantly optimized recursive function and you can get the same benefit. Functions are first class objects in python so you can, if you really want, write functional code. It just all kinda goes to shit when you start importing other people's libraries that demand classes and methods being used. Those libraries are just a major part of what benefit you get from using python. So I am not saying that any of this invalidates haskell or anything like that.
- DonaldPShimoda 7y ago> Tail call reversion optimization just exists so people can right 'for loops' in a manner that looks 'functional' on the surface. Uh... sorry, but I don't think that's right. The purpose of writing functions with tail recursion isn't "I want to write a for-loop but I want it to 'look' functional." The purpose of tail recursion is to make easy certain kinds of implementations. If you have a recursive data structure, using tail recursion over that structure is significantly more straightforward than writing iteratively. I actually wrote a comment about this recently: [0]. When using recursively-defined data, it makes more sense to write the traversal functions recursively. Functional programming makes significant use of recursion because, when dealing primarily with functions and immutable data, recursion is the simplest tool for the job. > Functions are first class objects in python so you can, if you really want, write functional code. It just all kinda goes to shit when you start importing other people's libraries that demand classes and methods being used. Those libraries are just a major part of what benefit you get from using python. I guess that's one way of looking at it, yeah. But if Python had better support (and encouragement) of functional programming, it seems to me that we'd have more functionally-styled libraries. [0] https://news.ycombinator.com/item?id=20896327 https://news.ycombinator.com/item?id=20896327
- kbp 7y ago> If you have a recursive data structure, using tail recursion over that structure is significantly more straightforward than writing iteratively. I actually wrote a comment about this recently: [0]. > [0] https://news.ycombinator.com/item?id=20896327 https://news.ycombinator.com/item?id=20896327 That example isn't tail recursive, though. The Python version is more difficult to read because you're using a manual stack instead of relying on the built-in one. An iterative algorithm, whether written using lexical recursion or a for loop, would entirely remove the use of a stack, not just hide the stack in your language implementation. Converting an iterative algorithm between the two forms is a simple syntax transformation, and doesn't introduce bookkeeping like that. Converting a body recursive function to iterate with an in-language stack introduces a lot of noise even if you use tail recursion to do the iteration. The tail recursive Haskell version of your Python isn't much better: sumTree :: BTree -> Int sumTree t = sumTree' [t] 0 where sumTree' [] total = total sumTree' (Leaf v : rest) total = sumTree' rest (v + total) sumTree' (Branch v l r : rest) total = sumTree' (l : r : rest) (v + total)