|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +# This Python script declares a binary lambda calculus interpreter. The |
| 5 | +# interpreter is written in untyped lambda calculus itself (i.e. only function |
| 6 | +# declarations and applications are allowed). Native recursion in particular |
| 7 | +# is disallowed. |
| 8 | + |
| 9 | +# All functions allow prefix notation; for example: |
| 10 | +# NOT (TRUE) |
| 11 | +# _1ST (tuple) |
| 12 | +# EQ (n) (m) |
| 13 | + |
| 14 | +# Utils |
| 15 | +# ===== |
| 16 | + |
| 17 | +# Implements recursion; for example: |
| 18 | +# FACTORIAL = Y (lambda f: lambda n: 1 if n == 0 else n * f(n-1)) |
| 19 | +# See https://en.wikipedia.org/wiki/Fixed-point_combinator#Fixed_point_combinators_in_lambda_calculus |
| 20 | +# for more. |
| 21 | +Y = lambda f: ( |
| 22 | + (lambda x: f (lambda y: x (x) (y))) |
| 23 | + (lambda x: f (lambda y: x (x) (y)))) |
| 24 | + |
| 25 | +ID = lambda x: x |
| 26 | + |
| 27 | +# Booleans |
| 28 | +# ======== |
| 29 | + |
| 30 | +TRUE = lambda x: lambda y: x |
| 31 | +FALSE = lambda x: lambda y: y |
| 32 | + |
| 33 | +NOT = lambda p: p (FALSE) (TRUE) |
| 34 | +AND = lambda p: lambda q: p (q) (FALSE) |
| 35 | +OR = lambda p: lambda q: p (TRUE) (q) |
| 36 | +XOR = lambda p: lambda q: p (NOT (q)) (q) |
| 37 | + |
| 38 | +# Tuples |
| 39 | +# ====== |
| 40 | + |
| 41 | +# Instantiate a 2-tuple by feeding 'TUPLE' with two elements. 'f' then allows |
| 42 | +# to retrieve the elements without 'b' applying to 'a'. |
| 43 | +TUPLE = lambda a: lambda b: lambda f: f (a) (b) |
| 44 | +_1ST = lambda t: t (FALSE) |
| 45 | +_2ND = lambda t: t (TRUE) |
| 46 | + |
| 47 | +# Integers |
| 48 | +# ======== |
| 49 | +# See https://en.wikipedia.org/wiki/Church_encoding#Calculation_with_Church_numerals |
| 50 | + |
| 51 | +# SUCC(n) = n+1 |
| 52 | +SUCC = lambda n: lambda f: lambda x: f (n (f) (x)) |
| 53 | +# PREC(n) = n-1 |
| 54 | +PREC = lambda n: lambda f: lambda x: (n |
| 55 | + (lambda g: lambda h: h (g (f))) |
| 56 | + (lambda u: x) |
| 57 | + (ID)) |
| 58 | + |
| 59 | +ADD = lambda m: lambda n: n (SUCC) (m) |
| 60 | +SUB = lambda m: lambda n: n (PREC) (m) |
| 61 | +TIMES = lambda m: lambda n: n (PLUS (n)) |
| 62 | + |
| 63 | +ZERO = FALSE |
| 64 | +ONE = SUCC (ZERO) |
| 65 | +TWO = SUCC (ONE) |
| 66 | +THREE = SUCC (TWO) |
| 67 | + |
| 68 | +# IS_ZERO(n) = n == 0 |
| 69 | +IS_ZERO = lambda n: n (lambda x: FALSE) (TRUE) |
| 70 | +# LEQ(n,m) = n <= m |
| 71 | +LEQ = lambda m: lambda n: IS_ZERO (SUB (m) (n)) |
| 72 | +# EQ(n,m) = n ==m |
| 73 | +EQ = lambda m: lambda n: AND (LEQ (m) (n)) (LEQ (n) (m)) |
| 74 | + |
| 75 | +# Lambda terms |
| 76 | +# ============ |
| 77 | + |
| 78 | +ENUM_3_1 = lambda a: lambda b: lambda c: a |
| 79 | +ENUM_3_2 = lambda a: lambda b: lambda c: b |
| 80 | +ENUM_3_3 = lambda a: lambda b: lambda b: c |
| 81 | + |
| 82 | +LAMBDA_VARIABLE_BUILDER = lambda n: TUPLE (ENUM_3_1) (n) |
| 83 | +LAMBDA_ABSTRACTION_BUILDER = lambda t: TUPLE (ENUM_3_2) (t) |
| 84 | +LAMBDA_APPLICATION_BUILDER = lambda x: lambda y: TUPLE (ENUM_3_3) (TUPLE (x) (y)) |
| 85 | + |
| 86 | +# Lambda lazy reduction |
| 87 | +# ===================== |
| 88 | + |
| 89 | + |
| 90 | +# The arguments are: |
| 91 | +# f a self-reference dummy function for the Y combinator. |
| 92 | +# e the lambda term to reduce. |
| 93 | +# x the lambda term which the current expression is bound to. |
| 94 | +# n the current De Bruijn index. |
| 95 | +# Illegal lambda terms will cause undefined behavior. |
| 96 | +REDUCE = Y (lambda f: lambda e: lambda x: lambda n: _1ST (e) |
| 97 | + # If the variable is bound to 'x', then we replace; if not, we leave it |
| 98 | + # there. |
| 99 | + (lambda g: LAMBDA_VARIABLE_BUILDER |
| 100 | + (EQ (n) (_2ND (e)) |
| 101 | + (x) |
| 102 | + (_2ND (e))) |
| 103 | + # We increment the counter 'n' and keep searching for terms bound to 'x'. |
| 104 | + (lambda g: LAMBDA_ABSTRACTION_BUILDER |
| 105 | + (f (_2ND (e)) (x) (SUCC (n)))) |
| 106 | + (lambda g: LAMBDA_APPLICATION_BUILDER |
| 107 | + # We reduce both terms and then the second is applied to the first. |
| 108 | + (f (_1ST (_2ND (e))) (x) (n)) |
| 109 | + # The dummy argument g=ID is just to avoid strict evaluation. |
| 110 | + (f (_2ND (_2ND (e))) (x) (n)))) (ID)) |
| 111 | + |
| 112 | +APPLY = Y (lambda f: lambda e: _1ST (e) |
| 113 | + # We only reduce applications. |
| 114 | + (LAMBDA_VARIABLE_BUILDER (_2ND (e))) |
| 115 | + (LAMBDA_ABSTRACTION_BUILDER (_2ND (e))) |
| 116 | + (REDUCE (_1ST (e)) (_2ND (e)) (ONE))) |
| 117 | + |
| 118 | +# TODO APPLY |
| 119 | +""" |
| 120 | +BINARY_LAMBDA = lambda p: p ( |
| 121 | + # p == 1 |
| 122 | + (lambda p1: Y_COMBINATOR (lambda f: lambda n:)) |
| 123 | + # p == 0 |
| 124 | + (lambda p1: p1 |
| 125 | + # pq == 01 |
| 126 | + (lambda p2:) |
| 127 | + # pq == 00 |
| 128 | + ) |
| 129 | +)""" |
0 commit comments