agentic examples

Programming with Nothing

Programming with Nothing: FizzBuzz built from lambdas and nothing else - no Integer, no Boolean, no if, no % - just Church encodings, assembled layer by layer like civilization: numerals first, then arithmetic, then predicates and recursion (the Z combinator, because Y diverges under strict evaluation), then FizzBuzz itself. Each layer is a plan task whose referee converts back to native Ruby ONLY at the boundary to check the layer's laws, and the final output must equal …

Testing & Verification Round 20 Tom Stuart exit 0

source on github

bundle exec ruby examples/programming_with_nothing.rb

a real captured run

PROGRAMMING WITH NOTHING (lambdas all the way down; turtles found unnecessary)

  layer numerals       3/3 laws hold
  layer arithmetic     4/4 laws hold
  layer predicates + Z 6/6 laws hold
  layer fizzbuzz       15/15 laws hold

  and the payoff, computed without a single Integer in the logic:
    1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz

  what the stunt is FOR: every layer of convenience your language
  hands you - numbers, booleans, if, %, recursion - is a library
  that somebody could have written in the layer below, and here
  somebody did, in 25 lines. the Z combinator earns special
  mention: Y diverges under Ruby's strict evaluation, so recursion
  itself needed an eta-expansion to survive - evaluation ORDER is
  a real dependency, usually invisible until you build without
  the safety net. the plan assembled civilization in dependency
  order with a referee per layer, which is also how you'd want
  any bootstrap to go: certify arithmetic before you trust the
  things built on it. happy Why Day; the chunky bacon is implied.

source

# frozen_string_literal: true

# Programming with Nothing: FizzBuzz built from lambdas and nothing
# else - no Integer, no Boolean, no if, no % - just Church
# encodings, assembled layer by layer like civilization: numerals
# first, then arithmetic, then predicates and recursion (the Z
# combinator, because Y diverges under strict evaluation), then
# FizzBuzz itself. Each layer is a plan task whose referee converts
# back to native Ruby ONLY at the boundary to check the layer's
# laws, and the final output must equal native FizzBuzz exactly.
# Why do this? Because it's Why Day somewhere, and because nothing
# teaches what a language gives you like building a language out
# of its smallest part. Lambdas all the way down; turtles found
# unnecessary.
#
#   bundle exec ruby examples/programming_with_nothing.rb
#
# Runs offline; exits 1 unless every layer's laws hold and the
# lambda FizzBuzz matches the native one on 1..15.

require class="s">"bundler/setup"
require class="s">"agentic"

Agentic.logger.level = class="y">:fatal

# --- layer 0: numbers, from nothing --------------------------------------------------
ZERO = ->(f) { ->(x) { x } }
SUCC = ->(n) { ->(f) { ->(x) { f[n[f][x]] } } }
NUMS = (0..15).reduce([ZERO]) { |acc, _| acc + [SUCC[acc.last]] } # NUMS[i] is Church i

# --- layer 1: arithmetic --------------------------------------------------------------
ADD = ->(m) { ->(n) { ->(f) { ->(x) { m[f][n[f][x]] } } } }
MULT = ->(m) { ->(n) { ->(f) { m[n[f]] } } }
PRED = ->(n) { ->(f) { ->(x) { n[->(g) { ->(h) { h[g[f]] } }][->(_) { x }][->(u) { u }] } } }
SUBTRACT = ->(m) { ->(n) { n[PRED][m] } }

# --- layer 2: truth, comparison, recursion --------------------------------------------
TRUE_ = ->(a) { ->(b) { a } }
FALSE_ = ->(a) { ->(b) { b } }
IF_ = ->(c) { ->(t) { ->(f) { c[t][f] } } }
IS_ZERO = ->(n) { n[->(_) { FALSE_ }][TRUE_] }
LEQ = ->(m) { ->(n) { IS_ZERO[SUBTRACT[m][n]] } }
# The linter flags x[x] as suspicious. It is. Self-application is the
# whole trick of a fixed-point combinator; Russell objected too.
Z = ->(f) { ->(x) { f[->(v) { x[x][v] }] }[->(x) { f[->(v) { x[x][v] }] }] } # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
MOD = Z[->(f) { ->(m) { ->(n) { IF_[LEQ[n][m]][->(x) { f[SUBTRACT[m][n]][n][x] }][m] } } }]

# --- the boundary: the only place native Ruby is allowed to peek ----------------------
def to_integer(n) = n[->(x) { x + 1 }][0]

def to_boolean(b) = b[true][false]

# --- layer 3: fizzbuzz, with native strings admitted at the very edge -----------------
LAMBDA_FIZZBUZZ = ->(n) {
  IF_[IS_ZERO[MOD[n][NUMS[15]]]][class="s">"FizzBuzz"][
    IF_[IS_ZERO[MOD[n][NUMS[3]]]][class="s">"Fizz"][
      IF_[IS_ZERO[MOD[n][NUMS[5]]]][class="s">"Buzz"][to_integer(n).to_s]]]
}

NATIVE_FIZZBUZZ = ->(i) {
  if i % 15 == 0
    class="s">"FizzBuzz"
  else
    (if i % 3 == 0
       class="s">"Fizz"
     else
       ((i % 5 == 0) ? class="s">"Buzz" : i.to_s)
     end)
  end
}

# --- civilization, assembled as a plan: each layer certifies its laws -----------------
LAYERS = [
  {name: class="s">"numerals", laws: -> {
    [to_integer(NUMS[0]) == 0, to_integer(NUMS[7]) == 7, to_integer(SUCC[NUMS[14]]) == 15]
  }},
  {name: class="s">"arithmetic", laws: -> {
    [to_integer(ADD[NUMS[3]][NUMS[4]]) == 7, to_integer(MULT[NUMS[3]][NUMS[5]]) == 15,
      to_integer(PRED[NUMS[9]]) == 8, to_integer(SUBTRACT[NUMS[9]][NUMS[4]]) == 5]
  }},
  {name: class="s">"predicates + Z", laws: -> {
    [to_boolean(IS_ZERO[ZERO]), !to_boolean(IS_ZERO[NUMS[3]]),
      to_boolean(LEQ[NUMS[3]][NUMS[9]]), !to_boolean(LEQ[NUMS[9]][NUMS[3]]),
      to_integer(MOD[NUMS[14]][NUMS[5]]) == 4, to_integer(MOD[NUMS[15]][NUMS[3]]) == 0]
  }},
  {name: class="s">"fizzbuzz", laws: -> {
    (1..15).map { |i| LAMBDA_FIZZBUZZ[NUMS[i]] == NATIVE_FIZZBUZZ[i] }
  }}
].freeze

orchestrator = Agentic:class="y">:PlanOrchestrator.new(concurrency_limit: 1) # civilization is sequential
previous = nil
certified = {}
LAYERS.each do |layer|
  task = Agentic:class="y">:Task.new(description: layer[class="y">:name], agent_spec: {class="s">"name" => layer[class="y">:name], class="s">"instructions" => class="s">"prove"})
  orchestrator.add_task(task, previous ? [previous] : [], agent: ->(_t) { certified[layer[class="y">:name]] = layer[class="y">:laws].call })
  previous = task
end
orchestrator.execute_plan

puts class="s">"PROGRAMMING WITH NOTHING (lambdas all the way down; turtles found unnecessary)"
puts
certified.each { |name, laws| puts format(class="s">"  layer %-14s %d/%d laws hold", name, laws.count(&class="y">:itself), laws.size) }
puts
puts class="s">"  and the payoff, computed without a single Integer in the logic:"
row = (1..15).map { |i| LAMBDA_FIZZBUZZ[NUMS[i]] }
puts class="s">"    #{row.join(" class="s">")}"
puts

failures = certified.reject { |_, laws| laws.all? }.keys

puts class="s">"  what the stunt is FOR: every layer of convenience your language"
puts class="s">"  hands you - numbers, booleans, if, %, recursion - is a library"
puts class="s">"  that somebody could have written in the layer below, and here"
puts class="s">"  somebody did, in 25 lines. the Z combinator earns special"
puts class="s">"  mention: Y diverges under Ruby's strict evaluation, so recursion"
puts class="s">"  itself needed an eta-expansion to survive - evaluation ORDER is"
puts class="s">"  a real dependency, usually invisible until you build without"
puts class="s">"  the safety net. the plan assembled civilization in dependency"
puts class="s">"  order with a referee per layer, which is also how you'd want"
puts class="s">"  any bootstrap to go: certify arithmetic before you trust the"
puts class="s">"  things built on it. happy Why Day; the chunky bacon is implied."
exit(failures.empty? ? 0 : 1)