
Coconut – Pythonic functional programming language - jonbaer
http://coconut-lang.org/
======
hcarvalhoalves
This is cool, but you can go pretty far w/ FP idioms in Python without
requiring a DSL/transpiler:

\- Toolz
([http://toolz.readthedocs.io/en/latest/](http://toolz.readthedocs.io/en/latest/))
provides the same primitives from Clojure (including partial functions,
compose, juxt, etc)

\- Pysistence
([https://pypi.python.org/pypi/pysistence/](https://pypi.python.org/pypi/pysistence/))
provides immutable data structs

\- For parallel map you can use [https://pypi.python.org/pypi/python-
pmap/1.0.2](https://pypi.python.org/pypi/python-pmap/1.0.2)

\- TCO is achievable w/ a decorator regardless of AST manipulation (like
Clojure's `loop/recur` construct)

The only thing left is pattern matching and more powerful destructuring
(Python already has limited support for it), I guess.

~~~
fiatjaf
No, you can't, because lambdas.

~~~
Lambdanaut
Could you elaborate on that?

~~~
merlincorey
Python lambdas are explicitly artificially limited to a single expression.
Python has statements which are not expressions, mostly control statements and
looping constructs.

~~~
fovc
The syntax isn't the greatest, but you can get pretty far with Python's
ternary expression. Generators in my experience remove the need for most
loops. Where lambdas feel really subpar for me is the lack of 'let' and error-
handling.

------
jpulec
While I resonate with the sentiment, I just wish Python would add better
syntax for functional programming. Having written a lot of JavaScript lately,
I wish Python's built-in functional tools supported something cleaner, kinda
like Underscore/Lodash.

~~~
Lord_DeathMatch
Specifically?

~~~
saadq
Not really a fan of the lambda syntax in Python. Comparison:

JavaScript:

    
    
      let arr = [1, 2, 3]
      let sumOfSquares = arr.map(n => n * n).reduce((a, b) => a + b) // 14
    

Python:

    
    
      arr = [1, 2, 3]
      sum_of_squares = reduce(lambda a, b: a + b, map(lambda n: n * n, arr)) # 14

~~~
brianwawok
Is there a more Pythonic way to do it? Lambdas are cool but usually not the
first place you go in Python. I would think something like (my best guess, not
a Python pro)...

    
    
        sum_of_squares = sum([x*x for x in arr])
    

Which I think is easier to read than either example post above.

Of course you will point out that this is less powerful than full map and
reduce.. but meh... pros and cons to both styles

~~~
akavi
What's the advantage of list comprehension over lambdas (assuming the lambda
syntax is decently lightweight)?

I feel like I come down hard on the side of lambdas, but I've never really
spent enough time in a language with list comprehension, so there's a good
chance I'm missing something.

~~~
warbiscuit
I'm not positive, but I think it saves the need to create a new execution
frame for each lambda call, since the whole loop executes in single frame used
by the comprehension.

In theory I suppose the VM could have a map() implementation which
opportunistically extracts the code from a lambda and inlines them when
possible; but doubt CPython does that. OTOH, I'd be surprised if PyPy _doesn
't_ do something like that.

~~~
fovc
Since Python 3, both generators and lists create a new stack frame. [1] (2nd
to last paragraph)

[1] [http://python-history.blogspot.com/2010/06/from-list-
compreh...](http://python-history.blogspot.com/2010/06/from-list-
comprehensions-to-generator.html)

~~~
warbiscuit
I'm not meaning when the comprehension is invoked, but during each iteration
of the loop within the comprehension.

When doing something like `map(lambda x: 2+x, range(100))`, there will be 101
frames created: the outer frame, and 100 for each invocation of the lambda.

Whereas `[2+x for x in range(100)]` will only create 2: one for the outer
frame, and one for the comprehension.

------
infodroid
> ...joining the over 30,000 people already using Coconut...

This triggered my BS detector.

It turns out 30,000 is the sum of all downloads on PyPI since the very first
version was released. But there are only 534 downloads on average for each
release since version 0.2.2, excluding the blip for 0.3.2. So there is no way
there are on the order of 30k users when downloads are roughly 1.8% of that
for each release.

This matters because exaggerated claims reduce trustworthiness in a project
and are off-putting to potential users.

~~~
Bromskloss
Since they just before that wrote that all valid Python is valid Coconut, I
thought that it would be a joke about every Python user being a Coconut user.

~~~
infodroid
Are you saying there are only around 30k Python users? :)

~~~
Bromskloss
Nah, when I got to the number, I realised that it wasn't going to be a joke
like I thought.

------
nailer
> Supports pipeline-style programming

>

> "hello, world!" |> print

Please follow the Elixir convention and call this by its proper name:
illuminati pyramid operator.

------
willtim
This is an impressive effort, but one major problem is that the Python runtime
lacks efficient support for tail calls. I see that there is an annotation for
self-recursion, but this is only a special case. Scala has a similar problem
with the JVM. Still, it does permit a nice functional syntax above perhaps
lower-level imperative code.

~~~
noobermin
That is a problem but it's not like Python is "efficient" or performant in
other areas that don't call an underlying C library.

------
aktiur
Your function in the tail recursion optimization exemple is actually not tail
recursive. Factorial needs an accumulator parameter to get that tail call.

~~~
agumonkey
Maybe the decorator rewrites the AST. I've seen this before.

~~~
santialbo
It does, when the function is actually tco optimizable. The example is not. On
the docs however it's correct
[http://coconut.readthedocs.io/en/master/DOCS.html#recursive](http://coconut.readthedocs.io/en/master/DOCS.html#recursive)

~~~
coconutlang
Good catch! I'll fix it right away.

------
doomrobo
The example algebraic data type isn't an algebraic data type at all. It's a
single type with a single type constructor. How is this different from a
regular class?

~~~
andolanra
It looks like _data_ declarations get translated to immutable subclasses of
_collections.namedtuple_ in such a way that you can match on the resulting
values. So it's really less like an algebraic data type and more like one of
Scala's case classes.

~~~
abecedarius
When I used namedtuple that way it tended to blow up on me, because
namedtuples implement 'dunder' methods to act as sequences, which is not what
I expect for some random algebraic datatype. This makes bugs manifest as very
head-scratching behavior. Nowadays I use my own struct type builder with only
the equivalent of 'derives Show' and such.

------
fuzzythinker
Interesting. Looks more polished than mochi[1] and hask[2].

[1] [https://github.com/i2y/mochi](https://github.com/i2y/mochi)

[2] [https://github.com/billpmurphy/hask](https://github.com/billpmurphy/hask)

------
rcarmo
Well, so far nobody's complained about Python 3 support yet :)

Seriously now, this looks pretty neat, and even includes Jupyter support.

I suspect it manipulates the AST like Hy, and I can't see any reason why it
shouldn't work on Pypy.

~~~
keenerd
It works with python3 just fine. The 'coconut-lang' and 'coconut-lang-git'
packages on the AUR both use py3.

------
a3n

      $ coconut
      Coconut Interpreter:
      (type "exit()" or press Ctrl-D to end)
      >>> match [head] + tail in [0, 1, 2, 3]:
          print(head, tail)
          
      CoconutParseError: parsing failed (line 2)
          print(head, tail)

------
akkartik
What are all the _$_ for? They seem ugly..

~~~
dkersten
Quoting from the tutorial:

    
    
        Second, the partial application. Think of partial application
        as lazy function calling, and $ as the lazy-ify operator,
        where lazy just means “don’t evaluate this until you need to”.
        In Coconut, if a function call is prefixed by a $, like in this
        example, instead of actually performing the function call, a
        new function is returned with the given arguments already 
        provided to it, so that when it is then called, it will be
        called with both the partially-applied arguments and the new
        arguments, in that order. In this case, reduce$((*))
        is equivalent to (*args, **kwargs) -> reduce((*), *args, **kwargs).
    

[https://coconut.readthedocs.io/en/master/HELP.html](https://coconut.readthedocs.io/en/master/HELP.html)
(about a third of the way down, just search for "$" and look near the first
occurrences)

I agree though, it makes the code look very... noisy.

~~~
guitarbill
I'm all for syntactic sugar, but my experience with teaching programming is
that Python is easy to pick up because of the lack of weird operators.
Decorators are an obvious example where many struggle; and this looks like
another.

It will take someone cleverer than me to come up with an alternative
solutions; I suspect they chose "$" because it isn't an operator in Python and
you have to do a lot of partial application, so the logic was probably "short
is better". But having something more intuitive would be great (more
pythonic?).

~~~
dkersten
Python has lots of weird operators.

List/dict comprehension, list slicing, * and __in arguments. Comma for tuples
(which make sense to me, but add a lot of magic, especially since, in my
experience, beginners often assume that the parentheses are what make tuples).
There 's many more examples, but those are the ones that immediately spring to
mind.

Reading actual production Python code is not any easier than many other
languages and certainly far from "executable pseudocode"

~~~
guitarbill
That's fair - * args and * * kwargs does feel like a hack, although denoting
"zero or more" as * has a bit of tradition. I'd have to disagree with
list/dict comprehension, pretty natural considering e.g Javascript/JSON also
use [] / {} for each, respectively (Obviously completely subjective).

I also get that there's only so many symbols to pick from. Would have been
cool if it was *, which is already used for arguments.

I think my larger point is that even the small decisions when making a
language matter. There isn't really right/wrong or better/worse, but IMO
Python operators feel more cohesive/uniform than Ruby or Perl. Also,
describing how operator usage feels is hard.

Edit: Asterisks causing italics.

------
codezero
How does this compare with Hy?

~~~
agumonkey
Hy doesn't push FP idioms AFAIK. It's a clojury in clothing lisp.

~~~
codezero
Oh, I should read more :) thanks!

------
arnarbi
I'd suggest fixing the front page, which claims ADT support which seems
untrue, and the tail recursive example isn't tail recursive at all. Errors
like that make this too easy to dismiss.

------
grondilu
Have you ever looked at Perl 6, by any chance? We have most of the features
you list, except for instance Tail Call Optimization, but we'd love to have
someone implementing it!

~~~
wodenokoto
I think the purpose of coconut is not to give the world new functional
programming features, but give you "real" functional programming with access
to pythons vast library.

------
skykooler
Is there a Pythonic declarative programming language?

~~~
PeCaN
Yep!
[https://sites.google.com/site/pydatalog/](https://sites.google.com/site/pydatalog/)

(Technically it's not a separate programming language, rather a lot of Python
magic.)

------
spriggan3
Reminds me Ocaml, a nice language that deserves more exposure on HN.

------
mrcactu5
Is coconut strongly typed? Is it closer to Haskell or to Lisp / ML?

~~~
bjz_
You probably mean _statically typed_.

~~~
coconutlang
Coconut is a superset of Python, and Python is dynamically typed, so Coconut
also has to be.

------
wentoodeep
Rise of the LISP.

------
miguelrochefort
What's "pythonic" exactly?

~~~
baldfat
I googled it for you:

[http://stackoverflow.com/questions/25011078/what-does-
python...](http://stackoverflow.com/questions/25011078/what-does-pythonic-
mean)

~~~
miguelrochefort
Doesn't really help in this context. What's the difference between "pythonic"
and "idiomatic"?

Does Coconut being pythonic mean it's written in idiomatic Python (then why is
it a different language?) or does it mean it's written in idiomatic Coconut?

I don't think they thought this through.

~~~
drostie
They just mean "this language looks in general like Python." You're
overthinking it.

It's like saying that JavaScript looks C-ish or Java-ish. It definitely
doesn't look exactly the same; if you see e.g.

    
    
        var result = [].slice.apply(vals, 0);
    

then you can be highly certain that that's JS and not Java or C. But the idea
of having blocks of code enclosed in curly braces, which are formed out of
statements usually delimited by semicolons (except for special forms like if-
statements and for-loops which don't need to be followed with a terminal
semicolon), etc. is very much a C-style thing.

------
jasonm23
Name issues. E.g. Google: Coconut-Lang cookbook ...

------
eddd
> There should be one-- and preferably only one --obvious way to do it.

[https://www.python.org/dev/peps/pep-0020/](https://www.python.org/dev/peps/pep-0020/)

~~~
baldfat
Over the years I have come to dislike that phrase more and more. I think it
hindered Python from growing.

Also Python 2 and Python 3 gives me at least two was to print :)

~~~
ferrari8608
I've never understood why that phrase applies to Python when the language
still has the map, filter, reduce, etc. functional functions. Generators and
comprehensions completely removed the need for those, yet the functions remain
even in Python 3.

On the subject of printing, there are actual reasons for having this duplicate
functionality. The main way to print is of course the print function in three,
or print statement in two. The print function was lacking the ability to flush
the buffer before that was introduced in three. You would have to call
sys.stdout.flush(), whereas now the print function includes the boolean
"flush" keyword argument. The second way to print is to call
sys.stdout.write(), which does not automatically append a newline. That
functionality was implemented in the print function with the "end" keyword
argument. You can pass it an empty string in place of the newline.

So for most use cases, print() is just fine. Sometimes you want finer grained
control, and for that you would use the sys module.

~~~
drostie
Python 3 at least changes the semantics of map and filter so that they are
generators.

Lazy sequences are the sort of thing that you don't care about until you need
to process a ten-million-line file (or whatever) and suddenly find that your
program is slowing down for pointless memory allocations up-front -- then they
become unbelievably important.

~~~
ferrari8608
Yes, you are absolutely correct. In our production code, which is currently
running 2.7, we make extensive use of the itertools versions of those
functions (imap, ifilter, etc.) for this reason. The itertools functions
behave exactly like the Python 3 builtins, as iterators. The memory footprint
is minimal, and they are just overall faster.

