Operators
Arithmetic, comparison, logical, compound assignment, conditional assignment, and increment/decrement operators in Soli.
Arithmetic Operators
Shovel Operator
The << (shovel) operator appends an element to an array. It modifies the array in place and returns the array.
# Append element to array
arr = [1, 2, 3]
arr << 4
print(arr); # [1, 2, 3, 4]
# Push strings
names = ["Alice", "Bob"]
names << "Charlie"
print(names); # ["Alice", "Bob", "Charlie"]
# Returns the array for chaining
result = [1] << 2 << 3
print(result); # [1, 2, 3]
Comparison Operators
Logical Operators
&&
Logical AND
Both conditions must be true.
age = 25
has_license = true
if age >= 18 && has_license
print("Can drive")
end
At least one condition must be true.
if is_weekend || is_holiday
print("Day off!")
end
Negates the condition.
if !is_raining
print("No umbrella needed")
end
String Concatenation.
# Concatenation
greeting = "Hello, " + "World!"; # "Hello, World!"
message = "Value: " + 42; # "Value: 42" (auto-conversion)
# String methods
text = " Hello, World! "
print(text.trim()); # "Hello, World!"
print(text.lstrip); # "Hello, World! "
print(text.rstrip); # " Hello, World!"
print(text.upper()); # " HELLO, WORLD! "
print(text.lower()); # " hello, world! "
print(text.len()); # 18
# Substring operations
s = "Hello, World!"
print(s.sub(0, 5)); # "Hello" (from index 0, length 5)
print(s.find("World")); # 7 (index of first occurrence)
print(s.contains("Hello")); # true
print(s.starts_with("Hell")); # true
print(s.ends_with("!")); # true
# String manipulation
print(s.replace("World", "Soli")); # "Hello, Soli!"
print(s.split(", ")); # ["Hello", "World!"]
print(s.chomp); # "Hello, World!" (removes trailing newline)
print(s.reverse); # "!dlroW ,olleH"
print(s.capitalize); # "Hello, world!"
print(s.swapcase); # "hELLO, wORLD!"
# String padding
print("hi".center(10)); # " hi "
print("hi".ljust(10)); # "hi "
print("hi".rjust(10)); # " hi"
# String queries
print(s.count("l")); # 3
print(s.gsub("l", "L")); # "HeLLo, WorLd!"
print(s.match("H(.*)o")); # ["Hello", "ell"]
print(s.scan("[aeiou]")); # ["e", "o", "o"]
print(s.tr("aeiou", "AEIOU")); # "HEllO, WOrld!"
# Character operations
print("A".ord); # 65
print("hello".bytes); # [104, 101, 108, 108, 111]
print("hello".chars); # ["h", "e", "l", "l", "o"]
print("a\nb\nc".lines); # ["a", "b", "c"]
# Partition and delete
print("hello-world-test".partition("-")); # ["hello", "-", "world-test"]
print("hello-world".delete("l")); # "heo-word"
print("hello".delete_prefix("hel")); # "lo"
print("hello".delete_suffix("lo")); # "hel"
# String encoding
print("hello".bytesize); # 5
# Truncate
print("hello world".truncate(5)); # "he..."
Use ?? to provide default values for null.
user = {"name": "Alice", "email": null}
# Traditional null check
email = user["email"]
if email == null
email = "unknown"
end
# Null coalescing operator
display_email = user["email"] ?? "unknown"
# Chaining with null values
city = user["address"]["city"] ?? "Unknown City"
# If any key in the chain is null/missing, returns "Unknown City"
Use &. for safe navigation — access properties or call methods on values that might be null without raising an error.
# Safe navigation operator
user = get_user # might return null
# Returns null if user is null, otherwise returns user.name
name = user&.name
# Chain safe navigation for nested access
city = user&.address&.city
# Call methods safely — returns null if user is null
greeting = user&.greet
# Combine with ?? for default values
display_name = user&.name ?? "Anonymous"
Creates a range of integers (exclusive end).
# Range creates an array
numbers = 1..5 # [1, 2, 3, 4]
# Use in for loops
for i in 0..10
print(i) # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
end
Compound Assignment Operators
Compound assignment operators combine an arithmetic operation with assignment. They modify the variable in-place.
| Operator | Description | Equivalent |
|---|---|---|
| += | Add and assign | a += b is a = a + b |
| -= | Subtract and assign | a -= b is a = a - b |
| *= | Multiply and assign | a *= b is a = a * b |
| /= | Divide and assign | a /= b is a = a / b |
| %= | Modulo and assign | a %= b is a = a % b |
score = 0
score += 10 # score is now 10
score -= 3 # score is now 7
score *= 2 # score is now 14
score /= 7 # score is now 2
score %= 2 # score is now 0
# Also works with floats
price = 19.99
price *= 0.9 # apply 10% discount
# String concatenation with +=
msg = "Hello"
msg += " World" # msg is now "Hello World"
Conditional Assignment
Conditional assignment operators only assign the right-hand side when the target meets a condition. The right-hand side is not evaluated when the assignment is skipped.
| Operator | Description | Equivalent |
|---|---|---|
| ||= | Assign if current value is falsy (null or false) | a ||= b is a = a || b |
| &&= | Assign if current value is truthy | a &&= b is a = a && b |
| ??= | Assign only if current value is null | a ??= b is a = a ?? b |
# ||= : provide a fallback for falsy values
name = null
name ||= "Anonymous" # name is now "Anonymous"
name = "Alice"
name ||= "Anonymous" # name stays "Alice"
# ??= : default only for null (preserves false / 0)
port = null
port ??= 8080 # port is now 8080
flag = false
flag ??= true # flag stays false (only null triggers ??=)
# &&= : only update when already set
user = load_user()
user &&= enrich(user) # skips enrichment when user is null
# Lazy default for hash keys
cache = {}
cache["key"] ||= expensive_lookup() # computed once, reused on repeat
Increment & Decrement
Postfix ++ and -- operators modify a variable by 1 and return the old value.
| Operator | Description | Returns |
|---|---|---|
| ++ | Postfix increment | Old value (before increment) |
| -- | Postfix decrement | Old value (before decrement) |
i = 0
i++ # i is now 1
i++ # i is now 2
old = i-- # old is 2, i is now 1
# Common pattern: loop counter
n = 0
while n < 5
println(n)
n = n + 1
end