Variables & Types
Variables
Variables are created on first assignment. There is no declaration step — no var, let, or type annotation. Frankie figures out the type from the value.
name = "Alice"
age = 30
score = 97.5
active = true
nothing = nil
Variable names are lowercase with underscores. A variable referenced before assignment raises a runtime error.
puts mystery # Runtime error: Undefined variable: mystery
Reassignment
A variable can be reassigned to any type at any time:
x = 42
x = "hello" # now a String — valid
x = [1, 2, 3] # now a Vector — still valid
Multiple Assignment (Destructuring)
Assign several variables at once from a vector:
a, b, c = [10, 20, 30]
puts a # 10
puts b # 20
puts c # 30
# Extra variables are set to nil
x, y, z = [1, 2]
puts z # nil
# Common use: functions that return multiple values
def min_max(v)
[min(v), max(v)]
end
lo, hi = min_max([3, 1, 4, 1, 5, 9])
puts "#{lo}..#{hi}" # 1..9
Hash Destructuring
Assign several variables at once from a hash:
user = {name: "Alice", age: 30, role: "admin"}
{name, age} = user
puts "name=#{name} age=#{age}"
rows = [{city: "Lima", country: "Peru"}, {city: "Tokyo", country: "Japan"}]
rows.each do |row|
{city, country} = row
puts "#{city}, #{country}"
end
Constants
Names written in UPPER_SNAKE_CASE are treated as constants. Reassignment prints a warning and the original value is preserved.
MAX_RETRIES = 3
PI = 3.14159
APP_NAME = "Frankie"
puts MAX_RETRIES # 3
MAX_RETRIES = 10 # Warning: MAX_RETRIES is a constant
puts MAX_RETRIES # 3 — unchanged
Enums (v1.18)
enum declares a named set of symbolic values. Members are their own names as strings — no hidden integers:
enum Status(pending, active, done)
puts Status.pending # pending
puts Status.values # [pending, active, done]
puts Status.include?("done") # true
case order_state
when Status.active
ship_it()
end
Because members are plain strings, enums round-trip cleanly through JSON, CSV, and databases. enum is a contextual keyword — a variable named enum keeps working.
Types
Frankie is dynamically typed — every value has a type, but variables do not. The type is determined by the value at the time it is used.
| Type | Example | Description |
|---|---|---|
Integer |
42, -7 |
Whole numbers, no size limit |
Float |
3.14, -0.5 |
Decimal numbers |
String |
"hello" |
UTF-8 text |
Boolean |
true, false |
Logical values |
Nil |
nil |
Absence of a value |
Vector |
[1, 2, 3] |
Ordered list |
Hash |
{name: "Alice"} |
Key-value map |
Lambda |
->(x) { x * 2 } |
Storable function |
Record |
Point(x: 3, y: 4) |
Named data object |
Type Checking
puts is_integer(42) # true
puts is_float(3.14) # true
puts is_string("hello") # true
puts is_vector([1,2,3]) # true
puts is_nil(nil) # true
puts is_bool(true) # true
Type Conversion
Frankie does not coerce types silently. Convert explicitly:
puts 42.to_s # "42" — Integer to String
puts 42.to_f # 42.0 — Integer to Float
puts "42".to_i # 42 — String to Integer
puts "3.14".to_f # 3.14 — String to Float
puts 3.9.to_i # 3 — Float to Integer (truncates)
# Standalone function forms
puts to_int("42") # 42
puts to_float("3.14") # 3.14
puts to_str(42) # "42"
Inside #{} interpolation, any value is converted to a string automatically — no .to_s needed:
n = 42
puts "The answer is #{n}" # The answer is 42
puts "Double is #{n * 2}" # Double is 84
puts "Type check: #{is_integer(n)}" # Type check: true
Truthiness
Only false and nil are falsy. Everything else — including 0 and "" — is truthy:
if 0 then puts "truthy" end # prints
if "" then puts "truthy" end # prints
if [] then puts "truthy" end # prints
if false then puts "truthy" end # does not print
if nil then puts "truthy" end # does not print
Nil Safety
Calling a method on nil raises a runtime error. The &. operator returns nil instead of crashing:
name = nil
puts name.upcase # Runtime error
puts name&.upcase # nil — safe
# Chains short-circuit at the first nil
puts name&.upcase&.reverse # nil
Scope
Frankie has simple lexical scope. Variables defined in the outer scope are readable inside functions and blocks. Variables assigned inside a function are local to that function.
x = 10
def show
puts x # readable — outer scope is accessible
end
show # 10
def modify
x = 99 # local to modify — does not affect outer x
end
modify
puts x # 10 — unchanged