diff --git a/ROADMAP.md b/ROADMAP.md index d5715ea..9fff6c4 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -50,6 +50,9 @@ Folders without a `test.sh` are skipped until backfilled. One language per step, each landing as its own PR with implementation, README, test.sh, and expected output, CI-green before merge. Order roughly by reach: +- [x] Bash — function names stand in for higher-order values; the complete + boolean truth tables and `ZERO`/`SUCC`/`PRED` numeral examples pass the + focused test and root harness locally. - [ ] Rust - [ ] C# - [ ] Swift diff --git a/languages/b/bash/README.md b/languages/b/bash/README.md new file mode 100644 index 0000000..c63554b --- /dev/null +++ b/languages/b/bash/README.md @@ -0,0 +1,41 @@ +# Bash Lambda Core + +This implementation uses GNU Bash functions to express the Lambda Core +booleans and Church numerals. It requires Bash 4 or newer and no external +packages. + +## Run + +```sh +bash lambda-core.bash +``` + +## How it works + +Bash functions cannot be stored and passed around as ordinary values, so this +implementation passes each function's name instead. Expanding a quoted variable +in command position, as in `"$boolean"`, calls the function with that name. + +- `church_true` returns its first argument and `church_false` returns its + second. `church_not`, `church_and`, and `church_or` combine those selectors + using their Church definitions. +- A numeral receives the name of a function and a starting value. + `church_zero` returns the starting value unchanged, while `church_succ` + applies the function once more. `church_one` is defined as `SUCC ZERO`. +- `church_pred` advances a `(previous, current)` pair once per numeral + application, then returns the previous value. That produces one fewer + application while keeping `PRED ZERO` at zero. + +Standard output contains the full boolean truth tables followed by examples of +`ZERO`, `ONE`, `SUCC`, and `PRED`. + +## Test + +From this folder, run: + +```sh +sh test.sh +``` + +The repository's root `run-tests.sh` compares that output with +`expected-output.txt` on every pull request. diff --git a/languages/b/bash/expected-output.txt b/languages/b/bash/expected-output.txt new file mode 100644 index 0000000..ff7bfcf --- /dev/null +++ b/languages/b/bash/expected-output.txt @@ -0,0 +1,18 @@ +BOOLEAN TRUE +BOOLEAN FALSE +BOOLEAN FALSE +BOOLEAN TRUE +BOOLEAN FALSE +BOOLEAN FALSE +BOOLEAN FALSE +BOOLEAN TRUE +BOOLEAN FALSE +BOOLEAN TRUE +BOOLEAN TRUE +BOOLEAN TRUE +0 +1 +2 +1 +0 +0 diff --git a/languages/b/bash/lambda-core.bash b/languages/b/bash/lambda-core.bash new file mode 100644 index 0000000..4f33d97 --- /dev/null +++ b/languages/b/bash/lambda-core.bash @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Church booleans are selectors. Bash has no first-class function values, so +# the name of a Bash function stands in for the function itself. +church_true() { + printf '%s\n' "$1" +} + +church_false() { + printf '%s\n' "$2" +} + +church_not() { + local boolean=$1 + "$boolean" church_false church_true +} + +church_and() { + local first=$1 + local second=$2 + "$first" "$second" church_false +} + +church_or() { + local first=$1 + local second=$2 + "$first" church_true "$second" +} + +read_bool() { + local boolean=$1 + "$boolean" 'BOOLEAN TRUE' 'BOOLEAN FALSE' +} + +# A Church numeral receives a function and a starting value, then applies that +# function a particular number of times. Function names again stand in for +# function values. +church_zero() { + : "$1" + printf '%s\n' "$2" +} + +church_succ() { + local numeral=$1 + local function=$2 + local value=$3 + local previous + + previous=$("$numeral" "$function" "$value") + "$function" "$previous" +} + +# ONE is derived from SUCC ZERO. TWO is a convenient name used by the PRED +# example below. +church_one() { + church_succ church_zero "$@" +} + +church_two() { + church_succ church_one "$@" +} + +church_increment() { + printf '%s\n' "$(( $1 + 1 ))" +} + +# PRED uses the usual "remember the previous value" idea. Its state is a pair +# written as previous:current. Each numeral application changes (a, b) into +# (b, f(b)); selecting the first value after n steps gives n - 1 applications. +# Bash variables are dynamically scoped, so church_pred_step can see the +# function selected by church_pred without using eval or creating source code. +church_pred_step() { + local state=$1 + local current=${state#*:} + local next + + next=$("$pred_function" "$current") + printf '%s:%s\n' "$current" "$next" +} + +church_pred() { + local numeral=$1 + local pred_function=$2 + local value=$3 + local state + + state=$("$numeral" church_pred_step "$value:$value") + printf '%s\n' "${state%%:*}" +} + +# The arguments to read_church form a Bash command: either a numeral function +# by itself or an operation followed by its already supplied numeral argument. +read_church() { + local -a numeral=("$@") + "${numeral[@]}" church_increment 0 +} + +# Boolean examples: constants, NOT, and complete AND/OR truth tables. +read_bool church_true +read_bool church_false + +read_bool "$(church_not church_true)" +read_bool "$(church_not church_false)" + +read_bool "$(church_and church_false church_false)" +read_bool "$(church_and church_true church_false)" +read_bool "$(church_and church_false church_true)" +read_bool "$(church_and church_true church_true)" + +read_bool "$(church_or church_false church_false)" +read_bool "$(church_or church_true church_false)" +read_bool "$(church_or church_false church_true)" +read_bool "$(church_or church_true church_true)" + +# Numeral examples: ZERO, ONE = SUCC ZERO, SUCC ONE, and predecessor at and +# above the zero boundary. +read_church church_zero +read_church church_one +read_church church_succ church_one +read_church church_pred church_two +read_church church_pred church_one +read_church church_pred church_zero diff --git a/languages/b/bash/test.sh b/languages/b/bash/test.sh new file mode 100644 index 0000000..4a8d0cc --- /dev/null +++ b/languages/b/bash/test.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -eu + +command -v bash >/dev/null 2>&1 || exit 42 + +dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +bash "$dir/lambda-core.bash"