PatLang for Ruby Programmers
For new readers
This page assumes you already know Ruby and maps that knowledge onto PatLang by contrast — of the "for X programmers" pages on this site, this is the closest match syntactically, but the object model underneath is genuinely different. If Ruby isn't your background, the Paradigms Guide is a better starting point.
Of the four languages covered in this series, Ruby is the closest relative — PatLang's do/end blocks and word-form keywords are a deliberate nod to Ruby's own readability-first syntax, and PatLang even ships a working Ruby transpiler as a self-hosted portfolio demo. But the resemblance is skin-deep in places that matter: PatLang does now have a real class keyword with single inheritance and mixins, but it's a much thinner, less dynamic layer than Ruby's — no open classes, no blocks-as-values, no method_missing, and a very different relationship with metaprogramming (see below).
The quick mental model
If Ruby's whole philosophy is "everything is an object, and objects can redefine how they respond to messages," PatLang's is closer to "everything is a value, and a small set of host functions gives you object-shaped mutable state when you actually want it." Ruby's flexibility comes from a rich, late-bound object model; PatLang's comes from real logic and goal-planning engines built into the language, and from a genuine runtime-extensible syntax mechanism that plays a similar role to Ruby's metaprogramming without touching the object model at all.
Syntax, side by side
# Ruby
def add(a, b)
a + b
end
x = 5
if x > 0
puts "positive"
elsif x < 0
puts "negative"
else
puts "zero"
end
i = 0
while i < 10
i += 1
end
# PatLang
make a function called add takes a, b returns sum
return a + b
end
let x = 5
if x > 0 then
print("positive")
elif x < 0 then
print("negative")
else
print("zero")
end
let i = 0
while i < 10 do
let i = i + 1
end
The family resemblance is real — do/end, word-form elif instead of elsif, no braces required — but PatLang requires an explicit return (Ruby's implicit last-expression return doesn't apply) and requires parentheses on calls like print(...) where Ruby would let you drop them.
Things Ruby has that PatLang doesn't
- Classes, single inheritance, and mixins exist now, but no open classes and no
method_missing. See the object model section below — real, but much less dynamic than Ruby's: no reopening a class after the fact, no dynamic dispatch fallback when a method isn't found. - No blocks as a language feature (
{ |x| ... }/do |x| ... endpassed implicitly to a method and invoked withyield). PatLang has closures (|x| { x + 1 }) as ordinary first-class values you pass explicitly — powerful, but a different mechanism with noyield-style implicit block. - No string interpolation (Ruby's
"#{x}"). Build strings with+concatenation; double-quoted strings support only\n \t \r \" \\escapes. - No
for/each/times/map-as-a-loop-construct, nobreak/next. Onlywhile. Ruby's whole enumerable-and-block iteration idiom doesn't have a direct equivalent — you write the loop and index yourself, or reach for the handle-based collection functions. - No exceptions (
begin/rescue/raise). A failing host call is fatal to the whole program; there's no way to catch and recover. - No open classes, no monkey-patching, no true metaprogramming in the Ruby sense (defining methods dynamically, reflecting on and modifying the object model at runtime). PatLang's nearest equivalent — genuinely extending what the language itself understands — is the syntax DSL mechanism below, which works at the grammar level, not the object model.
Objects, lighter than Ruby's object model
Under the hood, every object is still four host functions over a shared, string-keyed store:
let acct = new("Account")
set_var(acct, "balance", 100)
send(acct, "deposit", [50])
print(get(acct, "balance"))
PatLang also has a real class keyword now, and its trait mechanism maps onto Ruby's mixins almost directly — composed in by name, last-listed wins on a method collision, which is close to Ruby's own module-inclusion-order precedence:
class Nameable
make a function called label takes self returns msg
return "name:" + get(self, "name")
end
end
class Person
traits Nameable
field job = "unemployed"
end
let p = new("Person", "p1")
print(send(p, "label"))
Still no open classes (a class block is one declaration, not reopenable afterward), no method_missing-style dynamic dispatch fallback, and no true metaprogramming — a deliberately thin, inspectable layer, not Ruby's rich runtime-malleable object system reimplemented. A class declaration is optional, not a requirement: the plain new/get/set_var/send form above still works exactly as it always did for any class name that's never been formally declared. If your Ruby instinct is to reach for a module to mix in shared behaviour, the PatLang instinct is the same shape now — a traits line naming another class.
Runtime-extensible syntax instead of metaprogramming
Ruby programmers reach for metaprogramming — define_method, method_missing, DSL gems built on blocks — to make code read like the problem domain. PatLang's equivalent lives one level up, in the grammar itself: syntax NAME { ... } blocks genuinely extend what the parser understands, expanded at runtime before the ordinary lexer/parser ever sees the result. See the router DSL demo and the dynamic syntax demo — it's a different mechanism solving a similar "make this read naturally" problem, working on source text rather than on the object model.
Numbers are exact, not float-by-default
Ruby's / on two integers truncates (7 / 2 == 3) unless you explicitly use floats. PatLang's / on two integers that don't divide evenly produces the exact fraction — 7 / 2 is the Rational 7/2, never a truncated 3 and never a lossy 3.5. Integer overflow promotes automatically to an arbitrary-precision BigInt (closer to Ruby's own automatic Integer/Bignum unification than to C-family wraparound), and sqrt of a negative number promotes to Complex rather than raising. See the Numeric Tower section.
What PatLang has that Ruby doesn't
- Real logic programming, built in.
rule_add/solveis genuine Prolog-style backward-chaining resolution with backtracking — no external gem needed. - Real goal-oriented action planning (GOAP).
action_add/planfinds a genuinely cost-optimal ordered plan via forward search over actions with preconditions/effects/cost. - Real, verified parallelism. Ruby's MRI has a GIL limiting true parallel threading; PatLang's
parallel_mapspawns genuine OS threads with real concurrent execution, verified cross-thread-safe. - Runtime signals with no external process/IPC library. A running program answers a named signal from a second CLI invocation or a local network call directly — see the signals demo.
- Design by contract as real grammar (
require/ensure/assert), closer to Eiffel than to a Ruby gem. - A self-hosting compiler you can read. PatLang's own compiler is written in PatLang and compiles itself — genuinely legible in a way MRI's C internals aren't to most Ruby programmers.
See the build daemon exemplar for several of these working together in one real program, and the Ruby transpilation demo for the other direction — PatLang's own AST translated into idiomatic Ruby.
See also
Grammar & Syntax for the full, precise rules. Paradigms Guide for depth on every capability mentioned above. If you know another language too, see PatLang for Python Programmers, PatLang for Java Programmers, or PatLang for C Programmers.