Category Archives: Coconut

Coconut – utilities e considerazioni conclusive – 13

super-metal

Proseguendo da qui oggi copio qui, dove si parla di utilities. E poi concludo (per adesso).

Syntax Highlighting
Si può fare con Sublime Text, Pygments o altri metodi ancora.
Interessa? Pygments mi sembra sexy; non fosse che sono vecchio e per via di quello che adesso racconto.

Si può usare Coconut?
Secondo me sì ma ci sono alcune issues da addressare questioni da affrontare:

  • Coconut è nuovo e (per quel che ne so) personale, sviluppato dal solo Evan Hubinger, rockz 😀
  • sarebbe un altro linguaggio da imparare, sono già troppi;
  • per ragioni misteriose i linguaggi funzionali sono visti con diffidenza.

Illustro con un paio di esempi, spero indicativi.

c59

Quale delle due scritture è più naturale? Forse solo questione di abitudini ma…

Inoltre supponiamo che il codice Coconut sia contenuto nel file e.coco:

from math import exp
1 |> exp |> print

che compilo

c60

Il file risultante e.py è OK, nel senso che può essere eseguito

c61

ma è disastrosamente lungo e casinato

c62

Aprendolo e copiando le ultime due righe dentro Python (cosa che potrebbe capitare in caso di manutenzione, se si evita il file Coconut) si ottiene un codice eseguibile:

c63

quasi. Un caso più realistico (anche se sempre finto) è quello in cui il file .coco sia un modulo, come nel caso minimo val_const.coco:

from math import exp

def val_e():
    match t = 1 |> exp
    return t

che posso compilare e usare con Python:

c64

OK. Però, al solito il codice .py prodotto è lungo

c65

e –soprattutto– immodificabile all’interno di Python, per la manutenzione serve Coconut.

Quindi, in conclusione: Coconut mi piace, da monitorare, vedere se cresce. Per l’uso è da valutare all’interno del proprio ecosistema (cit.), è un altro linguaggio il cui apprendimento richiede risorse.
Mi è inoltre stato fatto notare che esistono progetti simili, chissà l’evoluzione… (visto che non cito Niels Bohr?) 😀

:mrgreen:

Coconut – built-ins – 12

Coj9

Continuo da qui, oggi su built-ins che semplificano il codice, qui.

addpattern
Takes one argument that is a pattern-matching function, and returns a decorator that adds the patterns in the existing function to the new function being decorated, where the existing patterns are checked first, then the new. Equivalent to:

def addpattern(base_func):
    """Decorator to add a new case to 
       a pattern-matching function, 
       where the new case is checked last."""
    
    def pattern_adder(func):
        def add_pattern_func(*args, **kwargs):
            try:
                return base_func(*args, **kwargs)
            except MatchError:
                return func(*args, **kwargs)
        return add_pattern_func
    return pattern_adder

c48

cosa che con Python … ahemmmm, già 😉

prepattern
Takes one argument that is a pattern-matching function, and returns a decorator that adds the patterns in the existing function to the new function being decorated, where the new patterns are checked first, then the existing. Equivalent to:

def prepattern(base_func):
    """Decorator to add a new case to
       a pattern-matching function, 
       where the new case is checked first."""
    def pattern_prepender(func):
        def pre_pattern_func(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except MatchError:
                return base_func(*args, **kwargs)
        return pre_pattern_func
    return pattern_prepender

c49

con Python –come già detto.

reduce
Coconut re-introduces Python 2’s reduce built-in, using the functools.reduce version.
Apply function of two arguments cumulatively to the items of sequence, from left to right, so as to reduce the sequence to a single value. For example, reduce((x, y) -> x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). The left argument, x, is the accumulated value and the right argument, y, is the update value from the sequence. If the optional initializer is present, it is placed before the items of the sequence in the calculation, and serves as a default when the sequence is empty. If initializer is not given and sequence contains only one item, the first item is returned.

c50

takewhile
Coconut provides itertools.takewhile as a built-in under the name takewhile.
takewhile(predicate, iterable)
Make an iterator that returns elements from the iterable as long as the predicate is true. Equivalent to:

def takewhile(predicate, iterable):
    # takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
    for x in iterable:
        if predicate(x):
            yield x
        else:
            break

L’esempio proposto mi da errore, forse dovuto alla versione 😦

Coconut:
negatives = takewhile(numiter, (x) -> x<0)

Python:
import itertools
negatives = itertools.takewhile(numiter, lambda x: x<0)


dropwhile
Coconut provides itertools.dropwhile as a built-in under the name dropwhile.

dropwhile(predicate, iterable)
Make an iterator that drops elements from the iterable as long as the predicate is true; afterwards, returns every element. Note: the iterator does not produce any output until the predicate first becomes false, so it may have a lengthy start-up time. Equivalent to:

def dropwhile(predicate, iterable):
    # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
    iterable = iter(iterable)
    for x in iterable:
        if not predicate(x):
            yield x
            break
    for x in iterable:
        yield x

Come per il caso precedente ottengo un errore.

Coconut:
positives = dropwhile(numiter, (x) -> x<0)

Python:
import itertools
positives = itertools.dropwhile(numiter, lambda x: x<0)

tee
Coconut provides itertools.tee as a built-in under the name tee.
tee(iterable, n=2)
Return n independent iterators from a single iterable. Equivalent to:

def tee(iterable, n=2):
    it = iter(iterable)
    deques = [collections.deque() for i in range(n)]
    def gen(mydeque):
        while True:
            if not mydeque:             # when the local deque is empty
                newval = next(it)       # fetch a new value and
                for d in deques:        # load it to all the deques
                    d.append(newval)
            yield mydeque.popleft()
    return tuple(gen(d) for d in deques)

Once tee() has made a split, the original iterable should not be used anywhere else; otherwise, the iterable could get advanced without the tee objects being informed.

This itertool may require significant auxiliary storage (depending on how much temporary data needs to be stored). In general, if one iterator uses most or all of the data before another iterator starts, it is faster to use list() instead of tee().

c51

consume
Coconut provides the consume function to efficiently exhaust an iterator and thus perform any lazy evaluation contained within it. consume takes one optional argument, keep_last, that defaults to 0 and specifies how many, if any, items from the end to return as an iterable (None will keep all elements). Equivalent to:

def consume(iterable, keep_last=0):
    """Fully exhaust iterable and return the last keep_last elements."""
    return collections.deque(iterable, maxlen=keep_last) 
                                    # fastest way to exhaust an iterator

In the process of lazily applying operations to iterators, eventually a point is reached where evaluation of the iterator is necessary. To do this efficiently, Coconut provides the consume function, which will fully exhaust the iterator given to it.

c52

count
Coconut provides a modified version of itertools.count that supports in, normal slicing, optimized iterator slicing, count and index sequence methods, repr, and _start and _step attributes as a built-in under the name count.
count(start=0, step=1)
Make an iterator that returns evenly spaced values starting with number start. Often used as an argument to map() to generate consecutive data points. Also, used with zip() to add sequence numbers. Roughly equivalent to:

def count(start=0, step=1):
    # count(10) --> 10 11 12 13 14 ...
    # count(2.5, 0.5) -> 2.5 3.0 3.5 ...
    n = start
    while True:
        yield n
        n += step

c53

Verifica:

c53-1

In Python can’t be done quickly without Coconut’s iterator slicing, which requires many complicated pieces. The necessary definitions in Python can be found in the Coconut header.

map and zip
Coconut’s map and zip objects are enhanced versions of their Python equivalents that support normal slicing, optimized iterator slicing (through __coconut_is_lazy__), reversed, len, repr, and have added attributes which subclasses can make use of to get at the original arguments to the object (map supports _func and _iters attributes and zip supports the _iters attribute).

c54

In Python can’t be done without defining a custom map type.

datamaker
Coconut provides the datamaker function to allow direct access to the base constructor of data types created with the Coconut data statement. This is particularly useful when writing alternative constructors for data types by overwriting __new__. Equivalent to:

def datamaker(data_type):
    """Returns base data constructor of data_type."""
    return super(data_type, data_type).__new__$(data_type)

Coconut:

data trilen(h):
def __new__(cls, a, b):
return (a**2 + b**2)**0.5 |> datamaker(cls)

Python:

import collections
class trilen(collections.namedtuple("trilen", "h")):
    __slots__ = ()
    def __new__(cls, a, b):
        return super(cls, cls).__new__(cls, (a**2 + b**2)**0.5)


recursive

Coconut provides a recursive decorator to perform tail recursion optimization on a function written in a tail-recursive style, where it directly returns all calls to itself. Do not use this decorator on a function not written in a tail-recursive style or the function will likely break.

c55

La REPL visualizza male la riga “raise TypeError("the argument must be an integer >= 0")“.

In Python can’t be done without a long decorator definition. The full definition of the decorator in Python can be found in the Coconut header.

parallel_map
Coconut provides a parallel version of map under the name parallel_map. parallel_map makes use of multiple processes, and is therefore often much faster than map. Use of parallel_map requires concurrent.futures, which exits in the Python 3 standard library, but under Python 2 will require python -m pip install futures to function.
Because parallel_map uses multiple processes for its execution, it is necessary that all of its arguments be pickleable. Only objects defined at the module level, and not lambdas, objects defined inside of a function, or objects defined inside of the interpreter, are pickleable. Furthermore, on Windows, it is necessary that all calls to parallel_map occur inside of an if __name__ == "__main__" guard.

parallel_map(_func, *iterables_)

Equivalent to map(func, *iterables) except func is executed asynchronously and several calls to func may be made concurrently. If a call raises an exception, then that exception will be raised when its value is retrieved from the iterator.

c56

e

c57

MatchError
A MatchError is raised when a destructuring assignment statement fails, and thus MatchError is provided as a built-in for catching those errors. MatchError objects support two attributes, pattern, which is a string describing the failed pattern, and value, which is the object that failed to match that pattern.

:mrgreen:

Coconut – Istruzioni – 11

no-fun-allowed

Non so se si userà Coconut ma rockz 😀 continuo da qui a copiare qui.

Istruzioni

Coconut supports significantly enhanced destructuring assignment, similar to Python’s tuple/list destructuring, but much more powerful. The syntax for Coconut’s destructuring assignment is

[match] <pattern> = <value> 

where <value> is any expression and <pattern> is defined by Coconut’s match statement. The match keyword at the beginning is optional, but is sometimes necessary to disambiguate destructuring assignment from normal assignment, which will always take precedence. Coconut’s destructuring assignment is equivalent to a match statement that follows the syntax:

match <pattern> in <value>:
    pass
else:
    err = MatchError(<error message>)
    err.pattern = "<pattern>"
    err.value = <value>
    raise err

If a destructuring assignment statement fails, then instead of continuing on as if a match block had failed, a MatchError object will be raised describing the failure.

c46

With Python can’t be done without a long series of checks in place of the destructuring assignment statement. See the compiled code for the Python syntax.

Decoratori
Unlike Python, which only supports a single variable or function call in a decorator, Coconut supports any expression.

L’esempio riportato non è completo e non ho trovato qualcosa di diverso da uno che seguirà prossimamente. Per cui riporto solo il codice senza eseguirlo.

Coconut:

@wrapper1 .. wrapper2 $(arg)
def func(x) = x**2

Python:

def wrapper(func):
    return wrapper1(wrapper2(arg, func))
@wrapper
def func(x):
    return x**2

Istruzioni else
Coconut supports the compound statements try, if, and match on the end of an else statement like any simple statement would be. This is most useful for mixing match and if statements together, but also allows for compound try statements.

Coconut:

try:
    unsafe_1()
except MyError:
    handle_1()
else: try:
    unsafe_2()
except MyError:
    handle_2()

Python:

try:
    unsafe_1()
except MyError:
    handle_1()
else:
    try:
        unsafe_2()
    except MyError:
        handle_2()

Istruzioni except
Python 3 requires that if multiple exceptions are to be caught, they must be placed inside of parentheses, so as to disallow Python 2’s use of a comma instead of as. Coconut allows commas in except statements to translate to catching multiple exceptions without the need for parentheses.

Coconut:

try:
    unsafe_func(arg)
except SyntaxError, ValueError as err:
    handle(err)

Python:

try:
    unsafe_func(arg)
except (SyntaxError, ValueError) as err:
    handle(err)


Lista di variabili

Coconut allows for the more elegant parenthetical continuation instead of the less elegant backslash continuation in import, del, global, and nonlocal statements.

c47

Codice passthrough
No, non so tradurlo (codice attraversante? orrendo!).
Coconut supports the ability to pass arbitrary code through the compiler without being touched, for compatibility with other variants of Python, such as Cython or Mython. Anything placed between \( and the corresponding close parenthesis will be passed through, as well as any line starting with \\, which will have the additional effect of allowing indentation under it.

Coconut:

\\cdef f(x):
    return x |> g

Python:

cdef f(x):
    return g(x)

:mrgreen:

Coconut – Notazione di funzione – 10

74

Coconut mi piace, tanto che continuando da qui oggi copio qui.

Operatori di funzione
Coconut uses a simple operator function short-hand: surround an operator with parentheses to retrieve its function. Similarly to iterator comprehensions, if the operator function is the only argument to a function, the parentheses of the function call can also serve as the parentheses for the operator function.

c46

A very common thing to do in functional programming is to make use of function versions of built-in operators: currying them, composing them, and piping them. To make this easy, Coconut provides a short-hand syntax to access operator functions.

La lista completa

(|>)     => # pipe forward
(|*>)    => # multi-arg pipe forward
(<|)     => # pipe backward
(<*|)    => # multi-arg pipe backward
(..)     => # function composition
(.)      => (getattr)
(::)     => (itertools.chain) # will not evaluate its arguments lazily
($)      => (functools.partial)
(+)      => (operator.__add__)
(-)      => # 1 arg: operator.__neg__, 2 args: operator.__sub__
(*)      => (operator.__mul__)
(**)     => (operator.__pow__)
(/)      => (operator.__truediv__)
(//)     => (operator.__floordiv__)
(%)      => (operator.__mod__)
(&)      => (operator.__and__)
(^)      => (operator.__xor__)
(|)      => (operator.__or__)
(<<)     => (operator.__lshift__)
(>>)     => (operator.__rshift__)
(<)      => (operator.__lt__)
(>)      => (operator.__gt__)
(==)     => (operator.__eq__)
(<=)     => (operator.__le__)
(>=)     => (operator.__ge__)
(!=)     => (operator.__ne__)
(~)      => (operator.__inv__)
(@)      => (operator.__matmul__)
(not)    => (operator.__not__)
(and)    => # boolean and
(or)     => # boolean or
(is)     => (operator.is_)
(in)     => (operator.__contains__)

c47

 

Creare scorciatoie di funzione
Coconut allows for shorthand in-line function definition, where the body of the function is assigned directly to the function call. The syntax for shorthand function definition is

def <name>(<args>) = <expr>

where <name> is the name of the function, <args> are the functions arguments, and <expr> evaluates the value that the function should return.

Note: Shorthand function definition can be combined with infix and pattern-matching function definition.

Coconut’s shorthand function definition is as easy to write as assignment to a lambda, but will appear named in tracebacks, as it compiles to normal Python function definition.

c48

Finzioni infisse
Coconut allows for infix function calling, where a function is surrounded by backticks and then can have arguments placed in front of or behind it. Backtick calling has a precedence in-between chaining and piping.

Coconut also supports infix function definition to make defining functions that are intended for infix usage simpler. The syntax for infix function definition is

def <arg> `<name>` <arg>:
    <body

where <name> is the name of the function, the <arg>s are the function arguments, and <body> is the body of the function. If an <arg> includes a default, the <arg> must be surrounded in parentheses.

Note: Infix function definition can be combined with shorthand and pattern-matching function definition.

A common idiom in functional programming is to write functions that are intended to behave somewhat like operators, and to call and define them by placing them between their arguments. Coconut’s infix syntax makes this possible.

c49

Con la tastiera italiana ` si ottiene con AltGr+' (nota per me che la memoria…) 😉

Pattern-matching di funzioni
Coconut supports pattern-matching / destructuring assignment syntax inside of function definition. The syntax for pattern-matching function definition is

[match] def <name>(<pattern>, <pattern>, ... [if <cond>]):
    <body>

where <name> is the name of the function, <cond> is an optional additional check, <body> is the body of the function, and <pattern> is defined by Coconut’s match statement. The match keyword at the beginning is optional, but is sometimes necessary to disambiguate pattern-matching function definition from normal function definition, which will always take precedence. Coconut’s pattern-matching function definition is equivalent to a match statement that looks like:

def <name>(*args):
    match (<pattern>, <pattern>, ...) in args:
        <body>
    else:
        err = MatchError(<error message>)
        err.pattern = "def <name>(<pattern>, <pattern>, ...):"
        err.value = args
        raise err

If pattern-matching function definition fails, it will raise a [MatchError]((#matcherror) object just like destructuring assignment.

Note: Pattern-matching function definition can be combined with shorthand and infix function definition.

c50

In Python can’t be done without a long series of checks at the top of the function. See the compiled code for the Python syntax.

:mrgreen:

 

Coconut – Espressioni

lovelace

Ho fatto un po’ di prove e Coconut funziona 😀
Deve crescere ma mi sa che lo userò per compiti particolari (p.es.: case) intanto continuo a studiarlo, copiando qui.

Lazy lists
Coconut supports the creation of lazy lists, where the contents in the list will be treated as an iterator and not evaluated until they are needed. Lazy lists can be created in Coconut simply by simply surrounding a comma-seperated list of items with (| and |) (so-called “banana brackets”) instead of [ and ] for a list or ( and ) for a tuple.

Lazy lists use the same machinery as iterator chaining to make themselves lazy, and thus the lazy list (| x, y |) is equivalent to the iterator chaining expression (x,) :: (y,), although the lazy list won’t construct the intermediate tuples.

Lazy lists, where sequences are only evaluated when their contents are requested, are a mainstay of functional programming, allowing for dynamic evaluation of the list’s contents.

c40

In Python can’t be done without a complicated iterator comprehension in place of the lazy list. See the compiled code for the Python syntax.
Ecco un altro caso in cui torna utile 😀

Applicazione parziale implicita
Coconut supports a number of different syntactical aliases for common partial application use cases. These are:

.attr         =>  operator.attrgetter("attr")
.method(args) =>  operator.methodcaller("method", args)
obj.          =>  getattr$(obj)
func$         =>  ($)$(func)
seq[]         =>  operator.__getitem__$(seq)
iter$[]       =>  # the equivalent of seq[] for iterators

c41

Vuoi vedere che manca un modulo…

c42

OK, come previsto 😀

Set Literals
Coconut allows an optional s to be prepended in front of Python set literals. While in most cases this does nothing, in the case of the empty set it lets Coconut know that it is an empty set and not an empty dictionary. Additionally, an f is also supported, in which case a Python frozenset will be generated instead of a normal set.

c43

Imaginary Literals
In addition to Python’s <num>j or <num>J notation for imaginary literals, Coconut also supports <num>i or <num>I, to make imaginary literals more readable if used in a mathematical context.

Imaginary literals are described by the following lexical definitions:
imagnumber ::= (floatnumber | intpart) ("j" | "J" | "i" | "I")

An imaginary literal yields a complex number with a real part of 0.0. Complex numbers are represented as a pair of floating point numbers and have the same restrictions on their range. To create a complex number with a nonzero real part, add a floating point number to it, e.g., (3+4i). Some examples of imaginary literals:
3.14i   10.i    10i     .001i   1e100i  3.14e-10i

c44

Underscore Separators
Coconut allows for one underscore between digits and after base specifiers in numeric literals. These underscores are ignored and should only be used to increase code readability. Comodo, pochi linguaggi lo usano, p.es. Ada.

c45

:mrgreen:

 

Una risposta & una domanda

woz

Una risposta a Jilla, forse se lo saranno chiesto anche altri: Coconut è nuovo ma promette bene. E l’uso che immediatamente viene in mente è quello di creare moduli con dentro le costanti e le funzioni che si intende usare, visto che permette di scrivere chiaramente e sinteticamente cose che con Python sono meno immediate, p.es. case.
Ecco un esempio (banalissimo, certamente nessuno scriverebbe cose così per davvero) di quali sono le mie intenzioni.

Il modulo coco_module.coco:

risposta = 42

def ciao(nome):
    print("Ehi", nome, "ciao!")

def doppio(n):
    return 2 * n

Che compilo ottenendo il modulo Python

r0

Non importa che coco_module.py sia lungo 298 righe, non devo editarlo, solo usarlo, nello script uso_c_m.py:

from coco_module import *

ciao("Juhan")
print(risposta, doppio(doppio(doppio(risposta))))

r1

Ovviamente –come già detto– quando il gioco si fa duro (inizio-cit.).

OK, lo ammetto: Coconut mi piace per i suoi aspetti funzionali che adesso pare siano trendyassay, quasi fossero pokémons. Forse perché contemporaneamente sto vedendo quell’altro linguaggio, di cui al momento non ricordo il nome, molto più tradizionale (nato come alternativa semplice del Fortran).


Siccome ho risposto, spero esaurientemente, alla domanda fattami ne avrei una anch’io (me). Chissà se…
Viene da un tweet, Why Linux sucks and will never compete with Windows or OSX.

Non so chi l’ha retwittato ma ho voluto vedere cos’è che non va con Linux.
Inizialmente ho classificato non tanto bene il post, l’autore del post, il titolare del blog, il retwittatore &co. poi però –in modo non esattamente razionale– ci ho rimuginato su. Non ho capito se l’autore sta lollando o cosa. Non ho capito cosa non gli va di Linux, sa usarlo, lo usa da tempo. Viene anche il dubbio che sia al servizio di NWO o Bilderberg o anche peggio (M$).

Sperando di non fargli pubblicità (segue 3 ed è seguito da 4 ma ha un blog) la mia domanda è: che dire del tipo e del tipo che ha linkato?

:mrgreen:

Coconut – keywords II

handshake

Continuo con le keywords, da qui, copiando qui.

case
Coconut’s case statement is an extension of Coconut’s match statement for performing multiple match statements against the same value, where only one of them should succeed. Unlike lone match statements, only one match statement inside of a case block will ever succeed, and thus more general matches should be put below more specific ones.

Each pattern in a case block is checked until a match is found, and then the corresponding body is executed, and the case block terminated. The syntax for case blocks is

case <value>:
    match <pattern> [if <cond>]:
        <body>
    match <pattern> [if <cond>]:
        <body>
    ...
[else:
    <body>]

where <pattern> is any match pattern, <value> is the item to match against, <cond> is an optional additional check, and <body> is simply code that is executed if the header above it succeeds. Note the absence of an in in the match statements: that’s because the <value> in case <value> is taking its place.

c36

In Python can’t be done without a long series of checks for each match statement. See the compiled code for the Python syntax.

c37

Al solito codice difficile da leggere, peraltro OK:

c38

Backslash-Escaping
In Coconut, the keywords data, match, case, async (keyword in Python 3.5), and await (keyword in Python 3.5) are also valid variable names. While Coconut can disambiguate these two use cases, when using one of these keywords as a variable name, a backslash is allowed in front to be explicit about using a keyword as a variable name.

c39

Variabili riservate
In Coconut, all variable names starting with _coconut are reserved. The Coconut compiler will modify and reference these variables with the assumption that the code being compiled does not modify them in any way. If your code does modify any such variables, your code is unlikely to work properly.

:mrgreen:

Coconut – keywords I

proxy

Continuo con le keywords, da qui, copiando qui.

match

Coconut provides fully-featured, functional pattern-matching through its match statements.

Overview
match statements follow the basic syntax match <pattern> in <value>. The match statement will attempt to match the value against the pattern, and if successful, bind any variables in the pattern to whatever is in the same position in the value, and execute the code below the match statement. match statements also support, in their basic syntax, an if <cond> that will check the condition after executing the match before executing the code below, and an else statement afterwards that will only be executed if the match statement is not. What is allowed in the match statement’s pattern has no equivalent in Python, and thus the specifications below are provided to explain it.

Syntax Specification
Coconut match statement syntax is

match <pattern> in <value> [if <cond>]:
    <body>
[else:
    <body>]

where <value> is the item to match against, <cond> is an optional additional check, and <body> is simply code that is executed if the header above it succeeds. <pattern> follows its own, special syntax, defined roughly like so:

pattern ::= (
    "(" pattern ")"                 # parentheses
    | "None" | "True" | "False"     # constants
    | "=" NAME                      # check
    | NUMBER                        # numbers
    | STRING                        # strings
    | [pattern "as"] NAME           # capture
    | NAME "(" patterns ")"         # data types
    | "(" patterns ")"              # sequences can be in tuple form
    | "[" patterns "]"              #  or in list form
    | "(|" patterns "|)"            # lazy lists
    | "{" pattern_pairs "}"         # dictionaries
    | ["s"] "{" pattern_consts "}"  # sets
    | (                             # head-tail splits
        "(" patterns ")"
        | "[" patterns "]"
      ) "+" pattern
    | pattern "+" (                 # init-last splits
        "(" patterns ")"
        | "[" patterns "]"
      )
    | (                             # head-last splits
        "(" patterns ")"
        | "[" patterns "]"
      ) "+" pattern "+" (
        "(" patterns ")"            # this match must be the same
        | "[" patterns "]"          #  construct as the first match
      )
    | (                             # iterator splits
        "(" patterns ")"
        | "[" patterns "]"
        | "(|" patterns "|)"        # lazy lists
      ) "::" pattern
    | pattern "is" exprs            # type-checking
    | pattern "and" pattern         # match all
    | pattern "or" pattern          # match any
    )

😳 panico?

Semantic Specification
match statements will take their pattern and attempt to “match” against it, performing the checks and deconstructions on the arguments as specified by the pattern. The different constructs that can be specified in a pattern, and their function, are:

  • Constants, Numbers, and Strings: will only match to the same constant, number, or string in the same position in the arguments.
  • Variables: will match to anything, and will be bound to whatever they match to, with some exceptions:
    • If the same variable is used multiple times, a check will be performed that each use match to the same value.
    • If the variable name _ is used, nothing will be bound and everything will always match to it.
  • Explicit Bindings (<pattern> as <var>): will bind <var> to <pattern>.
  • Checks (=<var>): will check that whatever is in that position is equal to the previously defined variable <var>.
  • Type Checks (<var> is <types>): will check that whatever is in that position is of type(s) <types> before binding the <var>.
  • Data Types (<name>(<args>)): will check that whatever is in that position is of data type <name> and will match the attributes to <args>.
  • Lists ([<patterns>]), Tuples ((<patterns>)), or lazy lists ((|<patterns>|)): will only match a sequence (collections.abc.Sequence) of the same length, and will check the contents against <patterns>.
  • Dicts ({<pairs>}): will only match a mapping (collections.abc.Mapping) of the same length, and will check the contents against <pairs>.
  • Sets ({<constants>}): will only match a set (collections.abc.Set) of the same length and contents.
  • Head-Tail Splits (<list/tuple> + <var>): will match the beginning of the sequence against the <list/tuple>, then bind the rest to <var>, and make it the type of the construct used.
  • Init-Last Splits (<var> + <list/tuple>): exactly the same as head-tail splits, but on the end instead of the beginning of the sequence.
  • Head-Last Splits (<list/tuple> + <var> + <list/tuple>): the combination of a head-tail and an init-last split.
  • Iterator Splits (<list/tuple/lazy list> :: <var>, or <lazy list>): will match the beginning of an iterable (collections.abc.Iterable) against the <list/tuple/lazy list>, then bind the rest to <var> or check that the iterable is done.

When checking whether or not an object can be matched against in a particular fashion, Coconut makes use of Python’s abstract base classes. Therefore, to enable proper matching for a custom object, register it with the proper abstract base classes.

Examples:

c31

Showcases else statements, which work much like else statements in Python: the code under an else statement is only executed if the corresponding match fails.

c32

Showcases matching to data types. Values defined by the user with the data statement can be matched against and their contents accessed by specifically referencing arguments to the data type’s constructor.

c33

Showcases how the combination of data types and match statements can be used to powerful effect to replicate the usage of algebraic data types in other functional programming languages.

c34

Showcases head-tail splitting, one of the most common uses of pattern-matching, where a + <var> (or :: <var> for any iterable) at the end of a list or tuple literal can be used to match the rest of the sequence.
E naturalmente il gioco può continuare (l’idea della pipe, Douglas McIlroy et al. (tanti al. anche prima) è fantastica):

c35

With Pythoncan’t be done without a long series of checks for each match statement. See the compiled code for the Python syntax.
Un’osservazione non so quanto valida: il codice prodotto dalla compilazione di Coconut è sempree molto poco leggibile, OK se è finale, altrimenti richiede pazienza, assay 😉

:mrgreen:

Coconut – operatori II

1892_5

Ci sono argomenti istituzionali come Octave (sì siamo in ritardo ma il caldo, le ferie, …) e ci sono argomenti che forse poi –chissà– come Coconut. Poi ce ne sono anche altri ancora, come SICP, che meriterebbero più attenzione. Avendo tempo. OK, oggi Coconut, continuo da qui a copiare qui.

Iterator Slicing
Coconut uses a $ sign right after an iterator before a slice to perform iterator slicing. Coconut’s iterator slicing works much the same as Python’s sequence slicing, and looks much the same as Coconut’s partial application, but with brackets instead of parentheses. It has the same precedence as subscription.
Iterator slicing works just like sequence slicing, including support for negative indices and slices, and support for slice objects in the same way as can be done with normal slicing. Iterator slicing makes no guarantee, however, that the original iterator passed to it be preserved (to preserve the iterator, use Coconut’s tee function).
Coconut’s iterator slicing is very similar to Python’s itertools.islice, but unlike itertools.islice, Coconut’s iterator slicing supports negative indices, and is optimized to play nicely with custom or built-in sequence types as well as Coconut’s map, zip, range, and count objects, only computing the elements of each that are actually necessary to extract the desired slice. This behavior can also be extended to custom objects if they define their __getitem__ method lazily and set __coconut_is_lazy__ to True.

c24

Nota: il manuale usa 10**100 ma mi da overflow 😳

With Python can’t be done without a complicated iterator slicing function and inspection of custom objects. The necessary definitions in Python can be found in the Coconut header.

Alternative Unicode
Coconut supports Unicode alternatives to many different operator symbols. The Unicode alternatives are relatively straightforward, and chosen to reflect the look and/or meaning of the original symbol.

→ (\u2192)                  => "->"
↦ (\u21a6)                  => "|>"
*↦ (*\u21a6)                => "|*>"
↤ (\u21a4)                  => "<|" 
↤* (\u21a4*)                => "<*|" 
⋅ (\u22c5)                  => "*"
↑ (\u2191)                  => "**"
÷ (\xf7)                    => "/"
÷/ (\xf7/)                  => "//"
∘ (\u2218)                  => ".."
− (\u2212)                  => "-" (only subtraction)
⁻ (\u207b)                  => "-" (only negation)
¬ (\xac)                    => "~"
≠ (\u2260) or ¬= (\xac=)    => "!="
≤ (\u2264)                  => "<=" 
≥ (\u2265)                  => ">="
∧ (\u2227) or ∩ (\u2229)    => "&"
∨ (\u2228) or ∪ (\u222a)    => "|"
⊻ (\u22bb) or ⊕ (\u2295)    => "^"
« (\xab)                    => "<<" 
» (\xbb)                    => ">>"
… (\u2026)                  => "..."
× (\xd7)                    => "@" (only matrix multiplication)

c25

OK, ma forse –sono vecchio– perché cambiare. E poi non è comodissimo introdurre il codice dopo C-S-u. Non sono riuscito a usare quelli con il codice esadecimale, per esempio xf7.

Keywords

data
The syntax for data blocks is a cross between the syntax for functions and the syntax for classes. The first line looks like a function definition, but the rest of the body looks like a class, usually containing method definitions. This is because while data blocks actually end up as classes in Python, Coconut automatically creates a special, immutable constructor based on the given arguments.

Coconut data blocks create immutable classes derived from collections.namedtuple and made immutable with __slots__. Coconut data statement syntax looks like:

data <name>(<args>):
    <body>

<name> is the name of the new data type, <args> are the arguments to its constructor as well as the names of its attributes, and <body> contains the data type’s methods.Subclassing data types can be done easily by inheriting from them in a normal Python class, although to make the new subclass immutable, the line

__slots__ = ()

Rationale
A mainstay of functional programming that Coconut improves in Python is the use of values, or immutable data types. Immutable data can be very useful because it guarantees that once you have some data it won’t change, but in Python creating custom immutable data types is difficult. Coconut makes it very easy by providing data blocks.

Python Docs
Returns a new tuple subclass. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with type names and field names) and a helpful __repr__() method which lists the tuple contents in a name=value format.

Any valid Python identifier may be used for a field name except for names starting with an underscore. Valid identifiers consist of letters, digits, and underscores but do not start with a digit or underscore and cannot be a keyword such as class, for, return, global, pass, or raise.

Named tuple instances do not have per-instance dictionaries, so they are lightweight and require no more memory than regular tuples.

c26

This will fail because data objects are immutable

c27

la versione Python

c28

Showcases the syntax, features, and immutable nature of data types.

c29

Nota: la riga dopo il decoratore viene scritta in modo bizzarro nella REPL, quella corretta è def size(Node(left, right)) = size(left) + size(right) ovviamente.

la versione Python

c30

Pausa, devo digerire il tutto; poi continuo sempre sulle keywords 😀
:mrgreen:

Coconut – operatori I

Chjgv5mUkAAZhoL

Continuo da qui l’esame di Coconut, qui.

Prima di tutto una nota che a me non interessa più di tanto ma chissà…

Supporto di IPython/Jupyter
If you prefer IPython (the python kernel for the Jupyter framework) to the normal Python shell, Coconut can be used as an IPython extension or Jupyter kernel.

Extension
If Coconut is used as an extension, a special magic command will send snippets of code to be evaluated using Coconut instead of IPython, but IPython will still be used as the default. The line magic %load_ext coconut will load Coconut as an extension, adding the %coconut and %%coconut magics. The %coconut line magic will run a line of Coconut with default parameters, and the %%coconut block magic will take command-line arguments on the first line, and run any Coconut code provided in the rest of the cell with those parameters.

Kernel
If Coconut is used as a kernel, all code in the console or notebook will be sent directly to Coconut instead of Python to be evaluated. The command coconut --jupyter notebook (or coconut --ipython notebook) will launch an IPython/Jupyter notebook using Coconut as the kernel and the command coconut --jupyter console (or coconut --ipython console) will launch an IPython/Jupyter console using Coconut as the kernel. Additionally, the command coconut --jupyter (or coconut --ipython) will add Coconut as a language option inside of all IPython/Jupyter notebooks, even those not launched with Coconut. This command may need to be re-run when a new version of Coconut is installed.

Operatori

Lambdas
Coconut provides the simple, clean -> operator as an alternative to Python’s lambda statements. The operator has the same precedence as the old statement.
In Python, lambdas are ugly and bulky, requiring the entire word lambda to be written out every time one is constructed. This is fine if in-line functions are very rarely needed, but in functional programming in-line functions are an essential tool.
Lambda forms (lambda expressions) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression (arguments) -> expression  yields a function object. The unnamed object behaves like a function object defined with:

def (arguments):
    return expression

Note that functions created with lambda forms cannot contain statements or annotations.

Esempio:

c18

Applicazione parziale (currying)
Partial application, or currying, is a mainstay of functional programming, and for good reason: it allows the dynamic customization of functions to fit the needs of where they are being used. Partial application allows a new function to be created out of an old function with some of its arguments pre-specified.

Python Docs
Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args. If additional keyword arguments are supplied, they extend and override keywords. Roughly equivalent to:

def partial(func, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = keywords.copy()
        newkeywords.update(fkeywords)
        return func(*(args + fargs), **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

The partial object is used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.

Esempio:

c19

😳 un messaggio alieno 😉

Pipeline
Coconut uses pipe operators for pipeline-style function application. All the operators have a precedence in-between infix calls and comparisons and are left-associative. All operators also support in-place versions. The different operators are:
(|>)  => pipe forward
(|*>) => multiple-argument pipe forward
(<|)  => pipe backward
(<*|) => multiple-argument pipe backward

Esempio:

c20

Compose
Coconut uses the .. operator for function composition. It has a precedence in-between subscription and exponentiation. The in-place operator is ..=.

Esempio:

c21

Chain
Coconut uses the :: operator for iterator chaining. Coconut’s iterator chaining is done lazily, in that the arguments are not evaluated until they are needed. It has a precedence in-between bitwise or and infix calls. The in-place operator is ::=.
A useful tool to make working with iterators as easy as working with sequences is the ability to lazily combine multiple iterators together. This operation is called chain, and is equivalent to addition with sequences, except that nothing gets evaluated until it is needed.

Python Docs
Make an iterator that returns elements from the first iterable until it is exhausted, then proceeds to the next iterable, until all of the iterables are exhausted. Used for treating consecutive sequences as a single sequence. Chained inputs are evaluated lazily. Roughly equivalent to:

def chain(*iterables):
    # chain('ABC', 'DEF') --> A B C D E F
    for it in iterables:
        for element in it:
            yield element

Esempio:
Can’t be done without a complicated iterator comprehension in place of the lazy chaining. See the compiled code for the Python syntax.

c22

Questo è il file conc.coco:

def N(n=0):
    return (0,) :: N(n+1) # no infinite loop because :: is lazy

(range(-10, 0) :: N())$[5:15] |> list |> print

compilo…

c23

295 righe non proprio leggibili, ecco le ulitime:

def N(n=0):
    return _coconut.itertools.chain.from_iterable((_coconut_lazy_item()
          for _coconut_lazy_item in (lambda: (0,), 
          lambda: N(n + 1)))) # no infinite loop because :: is lazy

(print)((list)(_coconut_igetitem(
          (_coconut.itertools.chain.from_iterable((_coconut_lazy_item() 
              for _coconut_lazy_item in (lambda: range(-10, 0), 
                    lambda: N())))), _coconut.slice(5, 15))))

Nota: ho spezzato le righe a sentimento per visualizzarle, troppo lunghe.

Pausa che devo riprendermi 😉
:mrgreen: