Coconut – Pythonic functional programming language
coconut-lang.org
coconut-lang.org
- Toolz (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/) provides immutable data structs
- For parallel map you can use 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.
Is it mimics the Erlang behaviour?
As I understand it, the primary limitation is that Python lambda only works with a single expression (I think it also has some weirdness regarding scope when calling functions?). I haven't poked seriously at Python since around about the time they were talking about removing it, so I probably have no idea what I'm talking about.
The idea is that generators and list comprehensions do the things most people do with lambdas in a more Pythonic way.
Coconut does both of those!
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.
A lambda can only contain a single expression, by "full anonymous function" I'm guessing hexane360 means multiple statements. You can't put a for loop or a context manager in a lambda for instance.
http://rosettacode.org/wiki/Runge-Kutta_method#using_lambda
It does not look as bad as one might expect, though the nesting of parenthesis makes things messy.
You can't get context managers or exception handling (although you can raise exceptions) into lambdas, I've tried.
def apply_ctx(ctx, func, *func_args, **func_kwargs):
with ctx as __ctx:
func(*func_args, **func_kwargs, ctx=__ctx)
but you can't do that itself as a lambda.
And I consider modifying the ast cheating :PNo disagreement, really depends whether you're a "rules" or "spirit" kind of person though.
def postfix(fn, *args):
return lambda arg: fn(arg, *args)
@postfix(map, range(5))
def result(i):
return i * i
print result
# [0, 1, 4, 9, 16]
(`postfix` is necessary because `map` takes its argument positionally so it's not possible to pass in the sequence with functools.partial)Yeah, though I suppose you could hack around that and get nearly-full functionality in lambdas if you built a library that either wrapped non-expression statements in functions or provided equivalent functions. There are obviously some statements that there aren't good solutions for in that direction.
OTOH, using named functions is in many cases more readable -- in the context of what is otherwise a normal Python codebase -- than the kind of lambdas that you can't easily write in Python. But I like the Coconut approach of but providing a more concise syntax for the kind of lambdas Python already supports.
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 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
sum_of_squares = sum(x*x for x in arr)
This makes use of https://www.python.org/dev/peps/pep-0289/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.
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.
[1] http://python-history.blogspot.com/2010/06/from-list-compreh...
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.
I'm from a non-list-comprehension background too, but recently started working a lot in a large python codebase, and have found the dict/list comprehensions to be beautiful. I'm a huge fan. It's a shame lambda syntax is not the best and it's generally crippled, but comprehensions are a great 80/20 compromise for handling most cases very cleanly.
In fact I had "reduce" appearing in the names of some of my variables so I used it less than 32 times, about 20 times in that project.
Could you show your reduce calls?
It's more than just stylistic.
But hey, if you want to use map when you actually need to do a parallel map, cool. But seems very very uncommon. ~ 1 in 10,000 maps I write.
from operator import mul, add
arr = [1, 2, 3]
sum_of_squares = reduce(add, map(mul, arr, arr)) (defn sum-of-squares [a] (reduce + (map #(* % %) a)))
(sum-of-squares [1, 2, 3]) ; => 14 _.chain(values)
.map(() => {})
.flatten()
.compact()
.uniq()
.value()
vs Python where doing the same thing becomes either a nested mess of function calls or comprehensions or a for loop. MyTable.objects.
filter(some_row__gt=5).
exlude(other_row='q').
order_by('other_row')
The Python iterable APIs have decided to use nesting rather than chaining, but you can still have an underscore-like API: https://github.com/serkanyersen/underscore.pyThe bigger problem remains: lambda functions are hideous in Python. map() will forever be ugly if you try to use it in the same way it is used in most functional languages.
result = pytoolz.pipe(values, map, flatten, compact, uniq, value)
# or
func = pytoolz.compose(value, uniq, compact, flatten, map)
results = func(values) pytoolz.curried.map(fn) values |> map(&({})) |> flatten |> compact |> uniq
Although, the closure syntax is a little clunky before you get used to it.>
> "hello, world!" |> print
Please follow the Elixir convention and call this by its proper name: illuminati pyramid operator.
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.
$ 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) 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 (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.
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?).
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"
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.
(Technically it's not a separate programming language, rather a lot of Python magic.)
http://stackoverflow.com/questions/25011078/what-does-python...
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.
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.Also Python 2 and Python 3 gives me at least two was to print :)
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.
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.
Hmm, I had never thought about it like that. Are there any articles, etc. you recommend about the differences or the advantages of one method over the other? It'd be nice to look into this more.
There is no difference between `map(func, values)` and `[func(x) for x in values]`, except for character count.
> Barry Warsaw, one of the core Python developers, once said that it frustrated him that "The Zen of Python" (PEP 20) is used as a style guide for Python code, since it was originally written as a poem about Python's internal design. [0]
I did a quick search for more background but didn't find anything.
[0]: https://github.com/amontalenti/elements-of-python-style#a-li...