Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions languages/b/bash/README.md
Original file line number Diff line number Diff line change
@@ -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.
18 changes: 18 additions & 0 deletions languages/b/bash/expected-output.txt
Original file line number Diff line number Diff line change
@@ -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
123 changes: 123 additions & 0 deletions languages/b/bash/lambda-core.bash
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions languages/b/bash/test.sh
Original file line number Diff line number Diff line change
@@ -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"
Loading