Oz is based on a core language with very few datatypes that can be extended into more practical ones through syntactic sugar.
Basic data structures:
'|'(2 '|'(4 '|'(6 '|'(8 nil)))) % as a record.
2|(4|(6|(8|nil))) % with some syntactic sugar
2|4|6|8|nil % more syntactic sugar
[2 4 6 8] % even more syntactic sugar
Those data structures are values (constant), first class and dynamically type checked. Variable names in Oz start with an uppercase letter to distinguish them from literals[4] which always begin with a lowercase letter.
Functions are first class values, allowing higher order functional programming:
fun {Fact N}
if N =< 0 then 1 else N*{Fact N-1} end
end
fun {Comb N K}
{Fact N} div ({Fact K} * {Fact N-K}) % integers can't overflow in Oz (unless no memory is left)
end
fun {SumList List}
case List of nil then 0
[] H|T then H+{SumList T} % pattern matching on lists
end
end
Functions may be used with both free and bound variables. Free variable values are found using static lexical scoping. Supports higher-order functions and anonymous functions; procedures are functions (using the "proc" keyword) that return no value.
When the program encounters an unbound variable it waits for a value. For example, below, the thread will wait until both X and Y are bound to a value before showing the value of Z.
thread
Z = X+Y
{Browse Z}
end
thread X = 40 end
thread Y = 2 end
The value of a dataflow variable cannot be changed once it is bound:
X = 1
X = 2 % error
Dataflow variables make it easy to create concurrent stream agents:
fun {Ints N Max}
if N == Max then nil
else
{Delay 1000}
N|{Ints N+1 Max}
end
end
fun {Sum S Stream}
case Stream
of nil then S
[] H|T then S|{Sum H+S T}
end
end
local X Y in
thread X = {Ints 0 1000} end
thread Y = {Sum 0 X} end
{Browse Y}
end
Because of the way dataflow variables work, it is possible to put threads anywhere in a program and guaranteed that it will have the same result. This makes concurrent programming very easy. Threads are very cheap: it is possible to have 100,000 threads running at once.
This example computes a stream of prime numbers using the trial division algorithm by recursively creating concurrent stream agents that filter out non-prime numbers:
fun {Sieve Xs}
case Xs of nil then nil
[] X|Xr then Ys in
thread Ys = {Filter Xr fun {$ Y} Y mod X \= 0 end} end
X|{Sieve Ys}
end
end
It is again possible to extend the declarative model to support state and object-oriented programming with very simple semantics. To create a new mutable data structure called Cells:
local A X in
A = {NewCell 0}
A := 1 % changes the value of A to 1
X = @A % @ is used to access the value of A
end
With these simple semantic changes, the whole object-oriented paradigm can be supported. With a little syntactic sugar, OOP becomes well integrated in Oz.
class Counter
attr val
meth init(Value)
val:=Value
end
meth browse
{Browse @val}
end
meth inc(Value)
val :=@val+Value
end
end
local C in
C = {New Counter init(0)}
{C inc(6)}
{C browse}
end
https://github.com/mozart/mozart2
Oz is a multiparadigm programming language, developed in the Programming Systems Lab at Université catholique de Louvain, for programming language education. It has a canonical textbook: Concepts, Techniques, and Models of Computer Programming.
Last modified 16 December 2024