Variables and Types in Icon
Learn about variables, data types, automatic type conversion, and null handling in Icon with practical Docker-ready examples
Icon takes a relaxed approach to variables and types. Variables don’t need declarations — just assign a value with := and the variable springs into existence. The type belongs to the value, not the variable, so any variable can hold any type at any time.
What makes Icon’s type system interesting is how it interacts with goal-directed evaluation. Unassigned variables have the special value &null, and Icon provides elegant operators to test for null that integrate with its success/failure model. Type conversions happen automatically in most contexts, keeping code concise while maintaining strong typing underneath.
In this tutorial, you’ll explore Icon’s built-in types, see how automatic conversion works, learn about the null value and its role in the language, and work with structured types like lists, sets, and tables.
Basic Variables and Types
Icon has a rich set of built-in types. Variables are untyped containers — the values they hold carry the type information. The type() function tells you what type a value is, and you can inspect it at any time.
Create a file named variables.icn:
procedure main()
# Variables are created by assignment
x := 42
name := "Icon"
pi := 3.14159
vowels := 'aeiou'
# The type() function returns a string naming the type
write("x = ", x, " (", type(x), ")")
write("name = ", name, " (", type(name), ")")
write("pi = ", pi, " (", type(pi), ")")
write("vowels = ", vowels, " (", type(vowels), ")")
# Unassigned variables have the value &null
write("y = (", type(y), ")")
# The null test operators: / succeeds if null, \ succeeds if non-null
if /y then write("y is null")
if \x then write("x is not null")
# Variables can change type freely
x := "now I'm a string"
write("x is now: ", x, " (", type(x), ")")
# Arbitrary precision integers
big := 2 ^ 64
write("2^64 = ", big, " (", type(big), ")")
# Negative numbers and zero
neg := -17
zero := 0
write("neg = ", neg, ", zero = ", zero)
end
This example covers Icon’s core scalar types: integers (with arbitrary precision), real numbers, strings, and csets (character sets). The cset type — written with single quotes — is unique to Icon and represents an unordered set of characters, which is useful for string scanning operations.
Automatic Type Conversion
One of Icon’s practical strengths is automatic type conversion. When an operation expects a certain type, Icon converts the value if possible. If the conversion can’t be done, the expression fails rather than raising an error — consistent with Icon’s goal-directed philosophy.
Create a file named variables_conversion.icn:
procedure main()
write("--- Automatic Conversion ---")
# String to number in arithmetic
result := "25" + 17
write("\"25\" + 17 = ", result, " (", type(result), ")")
# Number to string in concatenation
greeting := "Version " || 9
write(greeting, " (", type(greeting), ")")
# Integer to real in mixed arithmetic
mixed := 7 / 2.0
write("7 / 2.0 = ", mixed, " (", type(mixed), ")")
# Integer division (both operands are integers)
intdiv := 7 / 2
write("7 / 2 = ", intdiv, " (", type(intdiv), ")")
write()
write("--- Explicit Conversion ---")
# Explicit conversion functions
i := integer("99")
write("integer(\"99\") = ", i, " (", type(i), ")")
r := real(42)
write("real(42) = ", r, " (", type(r), ")")
s := string(3.14)
write("string(3.14) = ", s, " (", type(s), ")")
# numeric() converts to integer or real as appropriate
n1 := numeric("100")
n2 := numeric("3.5")
write("numeric(\"100\") is ", type(n1))
write("numeric(\"3.5\") is ", type(n2))
write()
write("--- Failed Conversion ---")
# Conversion failure integrates with goal-directed evaluation
if integer("hello") then
write("converted")
else
write("integer(\"hello\") failed - not a number")
# Using the default operator to handle conversion failure
val := integer("abc") | 0
write("integer(\"abc\") defaulted to: ", val)
end
Notice how failed conversions don’t crash the program. The expression integer("hello") simply fails, and Icon’s control structures handle failure naturally. The | operator provides an alternative value when the left side fails — a pattern you’ll use often in Icon.
Structured Types
Icon provides several built-in structured types: lists, sets, and tables. These are mutable, dynamically sized, and can hold values of any type.
Create a file named variables_structures.icn:
record Point(x, y)
procedure main()
write("--- Lists ---")
colors := ["red", "green", "blue"]
write("colors has ", *colors, " elements")
every write(" ", !colors)
# Lists can hold mixed types
mixed := [1, "two", 3.0, ["nested"]]
write("mixed types: ", type(mixed[1]), ", ", type(mixed[2]),
", ", type(mixed[3]), ", ", type(mixed[4]))
write()
write("--- Sets ---")
fruits := set(["apple", "banana", "cherry", "apple"])
write("fruits has ", *fruits, " unique elements")
insert(fruits, "date")
if member(fruits, "banana") then
write(" banana is in the set")
write()
write("--- Tables ---")
ages := table(0)
ages["Alice"] := 30
ages["Bob"] := 25
ages["Carol"] := 35
write("Alice is ", ages["Alice"])
write("Unknown defaults to ", ages["nobody"])
write()
write("--- Records ---")
p := Point(10, 20)
write("Point: (", p.x, ", ", p.y, ")")
p.x := 50
write("Modified: (", p.x, ", ", p.y, ")")
write()
write("--- Size Operator * ---")
write("string length: ", *"hello")
write("list size: ", *colors)
write("set size: ", *fruits)
write("table size: ", *ages)
end
The * operator returns the size of any collection or the length of a string. The ! operator generates all elements of a collection. These two operators work uniformly across all structured types, which is one of Icon’s elegant design choices.
Running with Docker
| |
Expected Output
Running variables.icn:
x = 42 (integer)
name = Icon (string)
pi = 3.14159 (real)
vowels = aeiou (cset)
y = (null)
y is null
x is not null
x is now: now I'm a string (string)
2^64 = 18446744073709551616 (integer)
neg = -17, zero = 0
Running variables_conversion.icn:
--- Automatic Conversion ---
"25" + 17 = 42 (integer)
Version 9 (string)
7 / 2.0 = 3.5 (real)
7 / 2 = 3 (integer)
--- Explicit Conversion ---
integer("99") = 99 (integer)
real(42) = 42.0 (real)
string(3.14) = 3.14 (string)
numeric("100") is integer
numeric("3.5") is real
--- Failed Conversion ---
integer("hello") failed - not a number
integer("abc") defaulted to: 0
Running variables_structures.icn:
--- Lists ---
colors has 3 elements
red
green
blue
mixed types: integer, string, real, list
--- Sets ---
fruits has 3 unique elements
banana is in the set
--- Tables ---
Alice is 30
Unknown defaults to 0
--- Records ---
Point: (10, 20)
Modified: (50, 20)
--- Size Operator * ---
string length: 5
list size: 3
set size: 4
table size: 3
Key Concepts
- No declarations needed — variables are created by assignment with
:=and can hold any type - Types belong to values — use
type()to inspect the type of any value at runtime &nulland null testing — unassigned variables are&null; use/xto test for null and\xto test for non-null- Automatic type conversion — Icon converts between strings, integers, and reals as needed, failing gracefully when conversion is impossible
- Character sets (csets) — single-quoted literals like
'aeiou'represent sets of characters, useful for string scanning - Structured types — lists, sets, tables, and records provide flexible data organization with uniform operators like
*(size) and!(generate elements) - Failed conversion = expression failure — invalid conversions don’t crash; they integrate with Icon’s goal-directed evaluation model
- Arbitrary precision integers — Icon integers have no size limit, supporting exact arithmetic on large numbers
Running Today
All examples can be run using Docker:
docker pull codearchaeology/icon:latest
Comments
Loading comments...
Leave a Comment