Maned

The combinator birds

Generated by tools/gen_example_pages.py from mnd_scripts/examples/example_lambda_birds.mnd — the output below is a real run, not a transcription. Regenerating the page reruns it.

bird-combinator lambda notation, and logical lambda expressions built from Church booleans.

The program#

# ============================================================================
# example_lambda_birds.mnd
# ----------------------------------------------------------------------------
# FUNCTIONALITY: bird-combinator lambda notation, and logical lambda
# expressions built from Church booleans.
#
# Maned's functional core is combinatory logic under Smullyan's bird names
# (identity, kestrel, kite, bluebird, cardinal, warbler, ...). What makes the
# language unusual is that in a POSTFIX language you do not need to *call* the
# combinators -- the stack operators ARE them. Combinatory logic and RPN are
# the same idea written twice:
#
#   BIRD          LAMBDA TERM          MANED POSTFIX      MEANING
#   identity  I   \x. x                x                    the value itself
#   kestrel   K   \x.\y. x             x y drop             keep the first
#   kite      KI  \x.\y. y             x y swap drop        keep the second
#   warbler   W   \f.\x. f x x         x dup f              feed x to f twice
#   cardinal  C   \f.\x.\y. f y x      x y swap f           flip the arguments
#   bluebird  B   \f.\g.\x. f (g x)    x g f                composition
#   thrush    T   \x.\f. f x           x f                  postfix itself
#
# `x g f` needs no combinator at all: juxtaposition in postfix IS composition.
# That is why B, T and I have no runtime cost here -- they are the notation.
#
# CHURCH BOOLEANS. In lambda calculus `true` is K and `false` is KI, and
# `if c then x else y` is just `c x y`. Maned is integer-quantized, so a
# predicate yields a 0/1 mask instead of a function, and selection becomes
# branch-free arithmetic that is identical on the CPU interpreter and on an
# FPGA worker:
#
#     select(c, x, y)  =  c*x + (1-c)*y        # c is 1 (K) or 0 (KI)
#
# Every line below RUNS. The reserved words `identity`, `kestrel`, `kite`,
# `mockingbird`, `bluebird`, `cardinal`, `starling`, `thrush`, `warbler`,
# `owl`, `compose`, `blackbird`, `psi`, `phoenix`, `vireo`, `ycombinator` are
# held by the grammar for the named-combinator runtime; the CPU lambda
# evaluator (src/interp/lambda.cpp) implements I, K, KI and compose as terms,
# but they are NOT yet reachable as operators from a flow body. Use the
# postfix spellings above -- they are the same functions.
#
# RUN: ../maned-run examples/example_lambda_birds.mnd
# ============================================================================
mnd::quantmax=1000;
mnd::quantmin=-1000;
mnd::quantres=0;

in::x = [[10,20],[30,40]];
in::y = [[99, 1],[ 7,77]];

calc::lambda_flow birds(x, y) {
    # --- I : identity ------------------------------------------------------
    # \x. x -- an expression that is just an operand binds that operand.
    x same =

    # --- K : kestrel, the constant function (and Church TRUE) --------------
    # \x.\y. x -- push both, discard the second.
    x y drop kestrel_keeps_x =

    # --- KI : kite, K applied to identity (and Church FALSE) --------------
    # \x.\y. y -- push both, swap, discard.
    x y swap drop kite_keeps_y =

    # --- W : warbler, the duplicator --------------------------------------
    # \f.\x. f x x -- `dup` supplies the same argument twice.
    x dup elemwise_add warbler_doubles =
    x dup matmul warbler_squares =

    # --- C : cardinal, argument flip --------------------------------------
    # \f.\x.\y. f y x -- `swap` before a non-commutative operator.
    x y elemwise_sub cardinal_before =
    x y swap elemwise_sub cardinal_after =

    # --- B : bluebird, composition ----------------------------------------
    # \f.\g.\x. f (g x) -- write g then f. No combinator token required.
    x transpose relu bluebird_relu_of_transpose =

    # --- Church-boolean conditional: `c x y` ------------------------------
    # The predicate is the lambda; the mask is its quantized reification.
    x y > c =                       # c = 1 where x > y, else 0  (K vs. KI)

    c x elemwise_mul k_branch =     # the TRUE  branch, gated by c
    1 c sub not_c =                 # logical negation, by arithmetic
    not_c y elemwise_mul ki_branch =# the FALSE branch, gated by (1-c)
    k_branch ki_branch elemwise_add selected =   # elementwise max(x, y)

    # The same shape with the branches exchanged is elementwise min(x, y),
    # which is the whole point: swapping K and KI swaps the branches.
    c y elemwise_mul k_swapped =
    not_c x elemwise_mul ki_swapped =
    k_swapped ki_swapped elemwise_add selected_other =

    # --- a two-level logical lambda ---------------------------------------
    # (x > y) AND (x > 15) -- conjunction of two predicates, then one select.
    x 15 > big =
    c big and both =
    both x elemwise_mul kept =
    1 both sub not_both =
    not_both 0 elemwise_mul zeroed =
    kept zeroed elemwise_add big_and_larger =
} return same, kestrel_keeps_x, kite_keeps_y,
         warbler_doubles, warbler_squares,
         cardinal_before, cardinal_after,
         bluebird_relu_of_transpose,
         c, selected, selected_other, big, both, big_and_larger;

The run#

$ maned-run mnd_scripts/examples/example_lambda_birds.mnd
flow 'birds' outputs:
same : [2x2] = [10, 20, 30, 40]
kestrel_keeps_x : [2x2] = [10, 20, 30, 40]
kite_keeps_y : [2x2] = [99, 1, 7, 77]
warbler_doubles : [2x2] = [20, 40, 60, 80]
warbler_squares : [2x2] = [700, 1000, 1500, 2200]
cardinal_before : [2x2] = [-89, 19, 23, -37]
cardinal_after : [2x2] = [89, -19, -23, 37]
bluebird_relu_of_transpose : [2x2] = [10, 30, 20, 40]
c : [2x2] = [0, 1, 1, 0]
selected : [2x2] = [99, 20, 30, 77]
selected_other : [2x2] = [10, 1, 7, 40]
big : [2x2] = [0, 1, 1, 1]
both : [2x2] = [0, 1, 1, 0]
big_and_larger : [2x2] = [0, 20, 30, 0]

Next#

© 2026 Fabiano Dicheti · Found a problem on this page? Open an issue