This post introduces the Coconut language, a functional superset of the Python language, which aims to create elegant, functional code while remaining in a familiar Python environment and libraries, and provides some illustrative examples.
", !" |> x -> x.replace('', 'Coconut') |> print
The Coconut language (at the time of this writing, its latest version is v1.5.0) is a function-oriented strict superset of the Python language, and therefore everything that is valid for Python is also valid for Coconut, while Coconut is transpiled to Python. In fact, Coconut is a playground for mastering the paradigm of functional programming, testing ideas in the field of FP, practicing techniques for solving problems in this paradigm and for educational purposes.
The language website page states that Coconut is designed to be useful to you. Coconut expands the Python programmer's repertoire by leveraging the tools of modern functional programming, making these tools easier to use and more powerful. In other words, Coconut does with functional programming what Python did with imperative programming.
Hopefully this post proves these claims in practice.
Just in case, you can install Coconut using the pip package manager: pip install coconut
Coconut is a strict superset of the Python language
Writing Python code in a functional style can be tricky, ranging from minor inconveniences, such as verbose lambda syntax, to more serious problems, such as chaining lazily computed iterators and pattern matching. Coconut is a functional superset of the Python language that aims to create elegant and functionally oriented Python-style code.
, Python . Python -, , . , . , .
2016 Python , - , Haskell Scala. Coconut , Python. , . , print(", !")
", !" |> print
. , Python, , (x) -> x2
lambda x: x2
.
, Coconut:
match [head] + tail in [0, 1, 2, 3]:
print(head, tail)
data Empty()
data Leaf(n)
data Node(l, r)
def size(Empty()) = 0
addpattern def size(Leaf(n)) = 1
addpattern def size(Node(l, r)) = size(l) + size(r)
{"list": [0] + rest} = {"list": [0, 1, 2, 3]}
range(10) |> map$(pow$(?, 2)) |> list
(| first_elem() |) :: rest_elems()
(f..g..h)(x, y, z)
x -> x ** 2
5 `mod` 3 == 2
", !" |> x -> x.replace('', 'Coconut') |> print
product = reduce$(*)
def factorial(n, acc=1):
case n:
match 0:
return acc
match _ is int if n > 0:
return factorial(n-1, acc*n)
range(100) |> parallel_map$(pow$(2)) |> list
coconut-develop
(pip install coconut-develop
) Python 3.10, Coconut. Coconut v1.6.0.
Coconut Python Coconut :
, Python, Coconut . , , , , Coconut , , , : Coconut Python, Python, Coconut.
coconut - , Python, . Python Coconut, , Python — - Python.
, Python , Coconut . , .
(Sieve of Eratosthenes) - n, . , , , . , ( ) .
Python
Python : primes
sieve
. primes
sieve
.
from itertools import count, takewhile
def primes():
def sieve(numbers):
head = next(numbers)
yield head
yield from sieve(n for n in numbers if n % head)
return sieve(count(2))
list(takewhile(lambda x: x < 60, primes()))
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]
sieve
count
, , 2 . sieve
(yield
) . (yield from
) sieve
, .
, numbers
next(numbers)
numbers
n for n in numbers if n % head
. , next
- : , .
list
, takewhile
, list
.
, - : , ..
Python Coconut
7 « »() Python Coconut.
1. lambda
lambda
->
.
from itertools import count, takewhile
def primes():
def sieve(numbers):
head = next(numbers)
yield head
yield from sieve(n for n in numbers if n % head)
return sieve(count(2))
list(takewhile(x -> x < 60, primes()))
2.
f(g(h(d)))
-: d -> h -> g -> f
|>
.
from itertools import count, takewhile
def primes():
def sieve(numbers):
head = next(numbers)
yield head
yield from sieve(n for n in numbers if n % head)
return sieve(count(2))
primes() |> ns -> takewhile(x -> x < 60, ns) |> list
3.
, , - . $
.
from itertools import count, takewhile
def primes():
def sieve(numbers):
head = next(numbers)
yield head
yield from sieve(n for n in numbers if n % head)
return sieve(count(2))
primes() |> takewhile$(x -> x < 60) |> list
4.
, yield
, yield
yield from
. , ::
.
from itertools import count, takewhile
def primes():
def sieve(numbers):
head = next(numbers)
return [head] :: sieve(n for n in numbers if n % head)
return sieve(count(2))
primes() |> takewhile$(x -> x < 60) |> list
5.
, , , , . ::
, . , .
from itertools import count, takewhile
def primes():
def sieve([head] :: tail):
return [head] :: sieve(n for n in tail if n % head)
return sieve(count(2))
primes() |> takewhile$(x -> x < 60) |> list
6.
. , . return
=
:
.
from itertools import count, takewhile
def primes() =
def sieve([x] :: xs) = [x] :: sieve(n for n in xs if n % x)
sieve(count(2))
primes() |> takewhile$(x -> x < 60) |> list
7.
, import
. , .. .
def primes() =
def sieve([x] :: xs) = [x] :: sieve(n for n in xs if n % x)
sieve(count(2))
primes() |> takewhile$(x -> x < 60) |> list
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59]
. : primes
sieve
, sieve
. :
from itertools import count, takewhile
def primes():
def sieve(numbers):
head = next(numbers)
yield head
yield from sieve(n for n in numbers if n % head)
return sieve(count(2))
list(takewhile(lambda x: x < 60, primes()))
:
def primes() =
def sieve([x] :: xs) = [x] :: sieve(n for n in xs if n % x)
sieve(count(2))
primes() |> takewhile$(x -> x < 60) |> list
, Coconut Haskell:
primes :: [Int]
primes = sieve [2..]
where
sieve (x :: xs) = x : sieve (filter (\n -> n `rem` x /= 0) xs
sieve [] = []
?> takewhile (<60) primes
def quick_sort([]) = []
@addpattern(quick_sort)
def quick_sort([head] + tail) =
""" ,
."""
(quick_sort([x for x in tail if x < head])
+ [head]
+ quick_sort([x for x in tail if x >= head]))
quick_sort([3,6,9,2,7,0,1,4,7,8,3,5,6,7])
[0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 7, 7, 8, 9]
def factorial(0, acc=1) = acc
@addpattern(factorial)
def factorial(n is int, acc=1 if n > 0) =
""" n!, n - >= 0."""
factorial(n-1, acc*n)
def is_even(0) = True
@addpattern(is_even)
def is_even(n is int if n > 0) = is_odd(n-1)
def is_odd(0) = False
@addpattern(is_odd)
def is_odd(n is int if n > 0) = is_even(n-1)
factorial(6) # 720
@recursive_iterator
def fib_seq() =
""" ."""
(1, 1) :: map((+), fib_seq(), fib_seq()$[1:])
fib_seq()$[:10] |> parallel_map$(pow$(?, 2)) |> list
[1, 1, 4, 9, 25, 64, 169, 441, 1156, 3025]
def zipwith(f, *args) =
zip(*args) |> map$(items -> f(*items))
list(zipwith(lambda x: x > 4, [1,2,3,4,5,6,7,8,9,0]))
[False, False, False, False, True, True, True, True, True, False]
I hope that the clarity of the above examples will arouse the interest of readers and encourage them to engage in a deeper study of the FP paradigm. In fact, Coconut offers syntactic sugar, i.e. a series of code coding optimizations that make code functional by being a playground for testing ideas using the functional programming paradigm.
Reference materials:
The post was prepared using information from the language website and materials from Anthony Kwong.