Coconut Lang

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 mypy and 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 data-records and 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.

So while reflecting on this predicament, I wondered “what if I could run elm in python”. It fits in my long term goals of writing a programming language, this would give me the ability to try the implementation with a well defined language. Whats more, Elm transpiles to javascript (a language famous for weak guarantees), so why not have it transpile to python? Before I got too far down the road, I wanted to check for prior work. I happened across upon this list. After perusing the one that stood out to me was coconut.

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

1
2
3
4
5
6
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.
Create a function that returns the Primorial of a number.
Examples
primorial(1) ➞ 2
primorial(2) ➞ 6
primorial(8) ➞ 9699690

I had posted an answer along the lines of

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from functools import reduce
from operator import mul
from itertools import count, islice

def is_prime(n: int) -> bool:
return n < 3 and not any (n % i == 0 for i in range(2, (n**.5)+1))

def product(*n: int) -> int:
return reduce(mul, n, 1)

def primorial(n: int) -> int:
all_nums = count()
primes = filter(is_prime, all_nums)
first_n = islice(primes, n)
return product(*first_n)

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 functional-pipeline

1
2
3
4
5
6
7
8
9
10
...

from functional_pipeline import pipeline, take

def primorial(n: int) -> int:
return pipeline(count(), [
(filter, is_prime),
(take, n),
product,
])

That feels a lot more elm-like and doesnt require the intermediate variables.

Lets take a look at coconut:

1
2
3
4
5
6
product = reduce $ (*)

match def is_prime(n is int if n < 2) = False
addpattern def is_prime(n is int) = not any (n % i == 0 for i in range(2, (n**.5)+1))

def primorial(n is int) = count() |> filter $ (is_prime) |> .$[:n] |> product

Okay lets break that down, first we can define product as a partial application of reduce. reduce $ (*) is functionally equivalent to

1
2
3
4
from functools import reduce, partial
from operator import mul

product = partial(reduce, mul)

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:

1
2
match def is_prime(n is int if n < 2) = False
addpattern def is_prime(n is int) = not any (n % i == 0 for i in range(2, (n**.5)+1))

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 match and addpattern. This selects the use case based on the arguments, which makes defining more complex logic a lot simpler.

Then lastly,

1
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 writing the return keyword.

So here we have the same pipeline as before, but I can compose a filter by partially applying is_prime with 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 |> pipes!

Another Example from Jornaya’s #python channel:

1
2
3
4
5
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.
Suppose this is your input:
Several Species of Small Furry Animals Gathered Together in a Cave and Grooving with a Pict
Your output would be:
a a and Animals Cave Furry Gathered Grooving in of Pict Several Small Species Together with

My original python

1
2
def sort_sentence(words: str) -> str:
return ' '.join(sorted(words.split(' '), key=lambda x:x.lower()))

And the resultant coconut

1
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.