Over the past few years, I have had grown to have a love-hate relationship with my primary language: python. The majority of this frustration with my first love stems from my other favorite language: Elm.
The primary point of contention between my two favorites comes down to developer experience. With elm, development is fun, and I feel like things just work when I expect them to. When reviewing code, I can look at the signatures to figure out how data is flowing and elm does a really good job of managing signal to noise when it comes to code readability. That and the fact that I can proof a solution to a problem makes developing a breeze and a joy.
Contrasting it with python. Now my python development experience isnt all that far off. I make heavy use of
yapf to replicate the type safety and elm-format experience. I make heavy use of dataclasses with
frozen=True to make sure that I am using immutable data wherever possible. I have written libraries like
functional-pipeline to try and emulate the experience of writing elm in python. And for the most part, I can get close. However a lot of the guarantees I get are weak, and trying to get other programmers on my team to write that way is a lot harder.
I have been trying to pick up Haskell or another pure fp and typed language to go on the backend, but it is hard to beat the Python Ecosystem for building, testing, deploying projects. I recently lamented to a coworker about how I feel the engineering disicipline has strayed away from Computer Science Purity in a lot of cases. My primary example was learning Rust. I know that I can write an API in rust, and have it be brilliantly fast, efficient and type safe. I know that I could deploy that Rust API to a server and have it run 100-1000x faster than the equivalent python code. However, due to trends like serverless, there is no point for that efficiency. It is easier for me to deploy a python API on a lambda, and pay near nothing for it, then it is for me to spend the time to write the best Rust based API and deploy it to a server. The server will cost me thousands of times more in most cases, and it would have been slightly slower development.
Coconut is a transpile to python functional programming language that is a superset of python. It adds a bunch of sytnax and helper functions, but ultimately any valid python is valid coconut. So I thought I would try it out for a few things.
One of the first problems I tried out came from a post in the
#python channel at work
A Primorial is a product of the first n prime numbers (e.g. 2 x 3 x 5 = 30). 2, 3, 5, 7, 11, 13 are prime numbers. If n was 3, you'd multiply 2 x 3 x 5 = 30 or Primorial = 30.
I had posted an answer along the lines of
from functools import reduce
Now to start off with, this is already a pretty clean and straightforward piece of
python. It is very declarative, it has a clean separation of concerns with it’s
functions, and overall very readable. I did take a pass using my
That feels a lot more elm-like and doesnt require the intermediate variables.
Lets take a look at coconut:
product = reduce $ (*)
Okay lets break that down, first we can define
product as a partial application
reduce $ (*) is functionally equivalent to
from functools import reduce, partial
Except in the coconut example, no imports are needed and we can use the operator itself instead of the named operator like we need to in python.
The next part:
match def is_prime(n is int if n < 2) = False
could have been defined exactly how I did it in python. The definition was already
a simple oneliner and was readable. However I decided to use the language feature
of guards and create a pattern matched function using
selects the use case based on the arguments, which makes defining more complex
logic a lot simpler.
def primorial(n is int) = count() |> filter $ (is_prime) |> .$[:n] |> product
So because I didnt metion it before, the
def <func_name>(<args>)= syntax makes
for a function with an implicit return. For simple functions this saves
So here we have the same pipeline as before, but I can compose a filter by partially
filter$(is_prime). I can define
take by partially
applying slice with
.$[:n]. So here is a lot of nice shortcuts for things we
do all the time in functional languages but are a pain in the ass in python. And did
I mention all of the beautiful
Another Example from Jornaya’s
Write a program that accepts a space-separated sequence of words as input and prints the words in a space-separated sequence after sorting them alphabetically.
My original python
def sort_sentence(words: str) -> str:
And the resultant coconut
def sort_sentence(sentence) = sentence |> .split(' ') |> sorted$(?, key=.lower()) |> " ".join
Here I get to use the
? in partial application and the
.lower() partial application,
both of these make the pipeline so much cleaner and easier to read in my opinion.
Overall I enjoy coconut, the code it produces it is a little less than readable, but it works and it executes in an python environment and can be intertwined with anything in python’s extensive ecosystem. I dont know if I would ever use it in production, but it has given me a respite on programming in non-provable systems.