Jupyter kernels

Jupyter kernels#

A Jupyter Notebook can utilise any program kernel that implements the Jupyter messaging protocol for executing code. There are kernels available for Python, Julia, Ruby, Haskell and many other languages.

In this notebook we demonstrate executing code with the Coconut Programming Language, a variant of Python built for simple, elegant, Pythonic functional programming.

In the first example we will define a recursive factorial function, a fundamentally functional approach that doesn’t involve any state changes or loops:

def factorial(n):
    """Compute n! where n is an integer >= 0."""
    case n:
        match 0:
            return 1
        match x is int if x > 0:
            return x * factorial(x-1)
    else:
        raise TypeError("the argument to factorial must be an integer >= 0")

3 |> factorial |> print
CoconutSyntaxWarning: deprecated case keyword at top level in case ...: match ...: block (use Python 3.10 match ...: case ...: syntax instead) (line 3)
  /~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  case n:
      match 0:
          return 1
      match x is int if x > 0:
          return x * factorial(x-1)
  else:
      raise TypeError("the argument to factorial must be an integer >= 0")

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
CoconutSyntaxWarning: found deprecated isinstance-checking 'x is int' pattern; rewrite to use class patterns (try 'int(x)') or explicit isinstance-checking ('x `isinstance` int' should always work) (line 6)
        /~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  match x is int if x > 0:
      return x * factorial(x-1)
  else:
      raise TypeError("the argument to factorial must be an integer >= 0")

  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
6

Although this example is very basic, pattern-matching is both one of Coconut’s most powerful and most complicated features.

In the second example, we implement the quick sort algorithm. This quick_sort algorithm works using a bunch of new constructs:

def quick_sort(l):
    """Sort the input iterator using the quick sort algorithm."""
    match [head] :: tail in l:
        tail = reiterable(tail)
        yield from quick_sort(left) :: [head] :: quick_sort(right) where:
            left = (x for x in tail if x < head)
            right = (x for x in tail if x >= head)
    # By yielding nothing if the match falls through, we implicitly return an empty iterator.

[3,0,4,2,1] |> quick_sort |> list |> print
[0, 1, 2, 3, 4]

Finally, we see that exceptions are raised as one would expect:

x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 x  #1: x

NameError: name 'x' is not defined