79 comments

[ 4.3 ms ] story [ 138 ms ] thread
(comment deleted)
Nice idea! An emerging alternative to this is Python support in Fable.

https://github.com/fable-compiler/Fable.Python

> Fable is a compiler that translates F# source files to JavaScript and Python.

interesting. So if you want to program python in a functional style, you can use Coconut to add functional features to python, or jump all the way to F# and just compile to python. Interesting to see two projects wind up in a similar place (compiling functional code to python) after starting in very different communities.

The major difference could be that type checking in coconut is optional.

Not sure what that means for productivity and type safety.

In F# you can type everything with `obj` and dynamic casting all over - not recommended but strictly speaking typing is optional there too.
And lambda still have 1 line ?

My style of programming is mostly using callback with anonymous function. This restriction prevents me to using Python in serious projects.

This is still 1 line, just separated by semicolon ?
And it only supports assignment statements and keyword statements. What’s a “keyword statement”?
You want lines, so that you may have multiple statements in your lambda. They've given you multiple statements, but using the semicolon instead.

I appreciate that won't work well for all use cases, but it'd eliminate a lot of the pain with the current lambda implementation.

For whatever reason, the Python community declared multiline lambdas as non-pythonic and this project aims to remain pythonic, so they went with semicolons instead.

I'm not a Python developer myself, but if that was the only thing holding you back, then perhaps that reason is now slightly reduced?

It does seem that if a function can’t fit with this new expanded capability, it probably is complex enough to warrant a name.

Assuming that Python’s support for nested functions carries over to Coconut, it seems like a reasonably-complete feature set.

If you really need more than one line this will probably work:

  L |> map$(def (x) -> (
    y = 1/x;
    y*(1 - y)
  ))
You can also make multiline python lambdas by using brackets. That is if you can fit your code into an expression.
What is the problem using def for your multiline closures?
It's boilerplate and makes you have to jump around in the code to follow what's happening.

If a function is used 2 times, 3 times, ... sure, go for it. If it's just a one-time use, please don't make a separate function.

It doesn't have to be a separate function, it can be nested (in fact it has to be if it's a closure). That's no different from what you would have got if you were allowed multiline lambdas in the first place. Or did you just mean it has to be one line earlier than the line of code that uses it? That doesn't seem like a big imposition to me.

    def precise_multiples_first(max_val, divisor):
        print(f"Using {max_val=}")
        def sort_key(val):
            if val % divisor == 0:
                return 0, val
            else:
                return 1, val
        return sorted(range(max_val), key=sort_key)

vs

    def precise_multiples_first(range_max, divisor):
        print(f"Using {max_val=}")
        return sorted(range(max_val), key=lambda val: (
            if val % divisor == 0:
                return 0, val
            else:
                return 1, val
        ))
(Yes I know I could've squeezed that function onto one line e.g. using ternary operator, but it was just a quick example.)
That only works if you are not already within a lambda expression - which happens quite often if you embrace a FP style
I always feel like functional programming languages start with some math major looking at traditional OOP code and thinking "ITS TOO READABLE!!"
I'm confused when people call it simple or elegant. I wonder if the same crowd would use similar words to describe regex.
Compared to writing the parsing in procedural code? Yes.

e.g.:

  a(b|c)d
vs something like:

  return str.StartsWith("a") && str.EndsWith("d") && str.Length == 3 && (str[1] == "b" || str[1] == "c");
Why wouldn't you consider regular expressions simple and elegant for the problem they are solving?
Maybe they mean syntax? I guess you could compare it to say syntax like "char 'a' + either char 'b' or char 'c' + char 'd'".

I like regex as is but that would def make it more approachable for the folks I know who don't.

That's a simple example. Things quickly get out of hand once you have a more complex problem like recognising a valid URL [1]:

`r"(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s`

Regex has a reputation for being "write once read never". It's primarily because it doesn't have any structure or "modularity". Functional programs also tend to pack many variables or operations in the same line without delimitation. For example Haskell's argument syntax.

[1] https://www.geeksforgeeks.org/python-check-url-string/

Sure, but I think your example goes way too far in the other direction. URLs are quite complex in comparison to a lot of pattern matching problems.

Also, what would be the procedural equivalent of the regex you linked? My guess is that if such a solution existed in procedural code, that this symbol soup you linked would be considered simple and elegant in comparison.

Procedural version is more verbose and quite long. That's the downside but it's also the reason why it'd be more readable and maintainable. Procedural version would actually be very similar to a parser combinator with elementary parsers being things like `while str[i].isalphanumeric(): i++`.
Or you could write it as a PEG (or indeed parser combinators) that would be even easier to read. I don't buy that verbosity on its own makes something readable and maintainable.
Imagine comparing diffs of a verbose parser vs diff between two complex regexes. The parser one could be 10x larger or even more, but it'd have a tractable diff. In the regex case I'd have to mentally keep track of column numbers and a bunch of other things.
PEG doesn't have that problem.
> Things quickly get out of hand once you have a more complex problem like recognising a valid URL

I'm not sure it's so complex. What's the equivalent code for recognising an URL? Regex are implemented in many languages, so this regex is, in a way, a portable library/function in most programming languages. And since it's shared, it can easily be checked way more than any single library would be.

Portability is an orthogonal concern. I agree with you that it's the reason regexes are the way they are but we could have also had better cross platform APIs.
Regexes are still better than cross platform APIs, they're an abstract concept. Just like you can implement a tree or a graph in any language and transfer your "tree skills" or your "graph skills", you can do so with regexes.
ok, now do the same check in procedural code like the comment you responded to did.
Ad absurdum - what do you think is easier to memorize - a story (eg The Iliad) or an equivalently explanatory regular expression?
Just trying to get an apples to apples comparison. He's using a relatively involved problem to say regex is difficult to parse. But the alternatives are libraries that hide the complexity, or large code blocks if you're rolling your own.
> Regex has a reputation for being "write once read never". It's primarily because it doesn't have any structure or "modularity"

I don't think this is true - it's because people don't write regex every day - I used to write a lot of web-scrapers which involved getting pretty handy with regexes, it's fine, trust me. If a regex is REALLY hard to read - it's normally because it's badly written, or because it's trying to do something genuinely complicated.

Also how are they not modular? You always build up a regex from smaller regexes, maybe the modularity isn't always easy to follow, but it is there. e.g.

{regex1}{regex2} = regex1 followed by regex2 (concatenation) ({regex1}|{regex2}) = regex2 OR regex2 (OR operator) etc.

Your URL example can be broken down into smaller modules - e.g. there's a 3-way OR block at the start that cover http(s)://, www(123)., abc.aa/ (don't understand the last one..)

Modularity exists but it's not made explicit. I need to mentally track the level of nesting, column number, suffixes (*+?) etc. If the structure was made explicit, it would be a lot more readable. And expressing the composition gradually, bottom up, comes very close to the procedural specification.
There's no chance you're going to convince me that throwing out regexes in favour of writing EVERYTHING procedurally is a good idea. I fully agree that they should often be built programatically to improve readability and reduce duplication. But capture groups, lookahead operators, kleene stars - these are a drag to do manually, not to mention you lose regex compiler optimisations - regex is the way.
Actually, what is the modularity concept in regexes? Is there a regex extension language where I can say "first match this then match that" but where I can name each part appropriately?
At some point, you’re probably better off using an actual parser. Parser combinator libraries make writing such things very easy.
To be really persuasive, I think we'd all like to see what a well-written URL checking procedure would look like when optimized for human reading.
regular expressions have structure. That's why you can concatenate two regular expressions a b to ab and you have a resulting expression that will match a string if a matches its first part and b matches the rest

Mainly what you're showing is that regular expresisons are awkward for matching certain types of syntax. You could have split each of the unique components in your expression into small parts and it would be much more readable.

Completely agree. My point is that having things broken down and built up gradually (aka procedural) is a better approach than one shot declarations.
This is basically code golfed. You could always break up a regex into named variables of its constituent logical parts; same with FP.

This is kind of like calling js ugly because you could come up with a minified js script that is really complex and fits in one line, or if you inlined all function calls in a long C file into the main() function body. There's a sweet spot of the complexity density per LOC that humans are seemingly more-or-less tuned for, where it's often possible to get a computer to handle more complexity-dense code.

Personally, the shorter the code and the stronger the types, the faster I can tell if I made a mistake and the fewer places there are for me to make one, and that's why I like terse FP languages.

If you aren’t writing verbose regexes and/or commenting the shit out of them, you’re really doing future self and others a disservice.
I'd call

    return str == "abd" || str == "acd"
_way_ more elegant and simple, to be honest. Because the actual code would look like

    return match("a(b|c)d", str)
err.... regex, which are a part of a larger field of formal language processing, which is elegant (but not simple). https://en.wikipedia.org/wiki/Regular_language The whole area of parsing and grammar is fascinating.
Expressivity is independent of specification. You can specify the same language in multiple ways and some are more readable than others
Do you know of a more readable regular language parser than regular expressions? News to me.
I watched a video explaining monads where the presenter explained that simple meant small…

So yeah, I think they would call regex simple as well.

It honestly never occurred to me before watching that video that someone could mean small when they say simple. I have always understood simple to mean “easy to understand” or “low complexity”.

My personal experience from working with functional languages at my university is that there is a certain familiarity-floor which has to be reached before you start to see the language as elegant. This doesn't even translate between languages - I've now worked with F#, Haskell, LISP and Erlang and all of them have required some practice before their elegance was visible to me, even though certain solutions seem to have a similar "shape" across these languages.
Kinda the other way around honestly. FP predates OO by decades. If you think of "traditional OOP" as C-style syntax, then you could conceivably think of C-style syntax as an attempt to make FP more readable.

Personally, I prefer FP-style (ML in particular) syntax. I think the fact that OOP languages are copying ML features like pattern matching, preference for immutability/call-by-value, and "do-notation" as a testament to FP's power.

Side note about syntax, honestly it's just glue. `<$>`, `|>`, `<*>`, and all the other symbols are just boring operations that are used so often that it's preferable to use symbols so programmers can focus on the meat. In other words:

    functionA <$> functionB |> functionC <*> functionD
is easier to read than

    functionA(map(functionB(pipe(functionC(bind(functionD))))))
This is kind of a surprisingly myopic/inflammatory comment to see on HackerNews. Both systems have their pros and cons, and people tend to be biased to prefer the one they learned first or worked extensively with first. And you don't have to choose; most languages nowadays support elements from both paradigms.

But honestly there's nothing even to argue, cause the comment doesn't really say anything.

Most languages I know don't really shine in both paradigms. Python even comes close, but it is shunned in OOP circles because of dynamic typing and loses street-cred in the FP hoods for its syntax.

C# feels clunky in both. F# is good in FP, has good interop with OOP, but why would you?

It definitely seems easier (or is more popular) to start people on imperative programming styles, because it is easier to make analogies. From there it's a quick jump to OOP.

> It definitely seems easier (or is more popular) to start people on imperative programming styles, because it is easier to make analogies

There are simply _way_ more 'imperative' programmers/teachersTtutorials/books/... than 'functional' ones.

Thinking back to my days of starting programming, it was a lot easier to get my head around "do this then that" rather than working in expressions and functional paradigms.

When teaching/instructing others, this impression only deepened.

It took a long while to even understand why functional programming has any appeal. I know that some university courses start out with scheme or lisp as a first language. But I suspect they get away with it because they have a captive audience and one that is already selected for high math tolerance.

Python loses cred in FP circles for being rather opinionated against FP. Map/filter being second-class to list comprehensions, reduce and partial being in functools, and no multi-line lambdas are the obvious/well-known examples of this, but more generally python has first class functions but doesn't make creating and manipulating functions ergonomic.
Where is the "no multi-line lambdas" myth coming from? I see this one a lot and its wrong. Python's lambdas can have as many lines as you want.

Did you mean to say that lambda expressions can't contain statements? But no Python expressions can! But functional programming is all done with expressions anyway, to the point where functional languages don't even have statements, so what's the problem?

No statements means no locals, which is sometimes bad for clarity and/or performance. Functional langs without statements have let-bindings.
That's really not true either. The lambda parameters are locals [a]. A let-binding is just a lambda that you call immediately. You can use defaults so the value doesn't have to appear as an argument [b]. Also, Python has assignment expressions now [c], and comprehensions are expressions which can have their own locals [d].

    >>> (lambda a, b=2:
    ...   print(a, a, b, b, c:=3, c, *[[d,d] for d in [4]][0])
    ... )(1)
    1 1 2 2 3 3 4 4
That's four different ways to get locals with no statements.
My wild guess is that GP comment was meant to "provoke" thoughtful comments in defence of FP. And regardless of GP's intend, it really succeeded in that regard - just looking at some of the responses.
Homepage feedback: It would be great if there was a demonstrative code-snippet that pre-populated that editor at the top of the page, even if it's just one of the snippets further down.
+1

Also, the editor should include an auto-print option that's on by default. The first snippets I tried (copying up from below) didn't include a print statement, and thus didn't show any output when run.

My boss pointed out and I concur, this looks a lot like the the Coffeescript of Python. That might be a good thing or a bad thing, depending on where you stand.
Similar principle. There is also Hylang and Fable (F#).

Python is a saner foundation to start from, though.

Nice! I was thinking I’d get round to trying some syntax-related experiments one day. Would this be an easier platform than (say) cpython for stuff like that?
Looks neat. I will check it out more deeply.

In other news, there is now a port of Fable that compiles F# to Python.

That sounds interesting. So I can program in F# and run it in Python? What are the limitations? Also, is the resulting code readable?
> So I can program in F# and run it in Python?

Yes.

> What are the limitations?

It's in alpha.

> Also, is the resulting code readable?

I think so, at least in this screenshot: https://user-images.githubusercontent.com/849479/104726627-3... from this PR https://github.com/fable-compiler/Fable/issues/2339

Its author gives a talk about it here, though you may need to make a (free) account: https://skillsmatter.com/skillscasts/17237-fable-python-f-sh...

He elaborates on the above screenshot and shows F# and Python running on the same Jupyter kernel.

I think one of the limitations is that the code is run in the Python VM, so that means you don't get the usual F# performance. That may matter for some stuff like recursions or numerical code, or for performance-sensitive things in general.
Python is the same language that promotes throwing exceptions for parsing a number from a string instead of a simple bool. Not exactly sure Python is the best language as a basis for FP, when Python itself so openly disregards such cleaner approaches.
Personally I'd rather the exception than a "simple" bool, which would make for noisy code and less simplicity. In reality and exception is just less bad, better than both would be a result type
Yes, if you have the luxury of starting anywhere. As someone who has to work in a Python-ish language for the sake of hiring and backward compatibility, I love Coconut.
Erlang is a successful FP language that throws exceptions for parsing a number from a string.

Admittedly Erlang also is built around the idea of exceptions everywhere.

I have wondered how something like ReasonML but with Python-like syntax instead of JavaScript-like syntax would look like, so this is pretty interesting.

While “any Python is valid coconut” seems appealing, in practice I don’t think this is the correct approach, since in my experience OO code is not really a good fit for the FP mental model of reasoning, and I would be happier with most of the OO features turned off.

Past related threads:

Coconut: Simple, elegant, Pythonic functional programming - https://news.ycombinator.com/item?id=23759721 - July 2020 (63 comments)

Coconut Programming Language - https://news.ycombinator.com/item?id=23663010 - June 2020 (1 comment)

Coconut Programming Language - https://news.ycombinator.com/item?id=23339351 - May 2020 (2 comments)

Coconut: Pythonic functional programming - https://news.ycombinator.com/item?id=18815125 - Jan 2019 (169 comments)

Coconut – Pythonic functional programming language - https://news.ycombinator.com/item?id=11960692 - June 2016 (130 comments)

Show HN: Coconut – Simple, elegant, Pythonic functional programming - https://news.ycombinator.com/item?id=11936038 - June 2016 (2 comments)