Steve's poplog primer - GetPoplog/Seed GitHub Wiki
For people who already know programming you can make a few framing statements like: Pop-11 is a dynamically typed language with full garbage collection, it is not object oriented but does have OOP libraries (it largely pre-dates the OO revolution), it supports full lexical binding but by default uses an older system called dynamic shallow binding, it includes an extremely powerful coroutine facility but lacks async facilities.
And then get into details like: top-level variables are normally declared with "vars" e.g.
vars x = 99;
And top-level functions (procedures) are defined with the define ... enddefine
syntax. e.g.
define factorial( n ); if n <= 1 then 1 else n * factorial( n - 1 ) endif enddefine;
Conditionals are written using if
or unless
, the latter being a shortcut for if not( ... )
. Like the majority of Pop-11 forms the opening keyword is balanced
with a closing keyword i.e. endif
or endunless
. The "cascaded" conditional
syntax is supported via elseif
and elseunless
, e.g.
if TEST1 then
STATEMENTS1
elseif TEST2 then
STATEMENTS2
elseunless TEST3 then
STATEMENTS3
else
STATEMENTS4
endif
Loops are always introduced with the for syntax word but there are a variety of forms:
vars i;
for i from A to B do ... endfor ;;; A to B inclusively
Note that Pop-11 does not use 0-indexing and half-open intervals but 1-indexing with fully closed intervals.
IMPORTANT: loop variables are not automatically declared, which is a hangover of Pop-11 being an older language.
Iteration over an iterable is done with the for ... in
syntax:
vars i;
for i in LIST do
STATEMENTS
endfor
In addition to these forms Pop-11 also has a while TEST do STATEMENTS endwhile
form. It also has an until ... do ...
form but this is merely equivalent to
while not( ..... ) do ..
And it also has a specialised form for repeating a fixed number of times:
repeat N times STATEMENTS endrepeat
And a repeat-forever form
repeat STATEMENTS endrepeat
Local variables are normally declared using the lvars
syntax. e.g.
define longest( string_list );
lvars s;
lvars max_so_far = -1;
lvars best_so_far = false;
for s in string_list do
lvars n = length( s );
if n > max_so_far then
s -> best_so_far;
n -> max_so_far
endif
endfor;
return( best_so_far )
enddefine;
lvars
is mnemonic for 'lexical vars'. Lexical variables in Pop-11 are always
local to some scope. Even when they are used at top-level they are local to the
scope of the compilation-unit. In practical terms the compilation-unit is the
same thing as a file (for advanced programmers: it's actually the scope of a
sysCOMPILE
). Because of this "lvars" are frequently used at top-level to define
private variables.
Pop-11 supports several non-structured syntax forms: return
, nextloop
(like
continue
in C syntax), quitloop
(like break
in C syntax). The return
keyword performs an immediate exit from the current dynamic scope, exactly the
same as in other languages. But if you want to return a value it must be
enclosed in parentheses: return( EXPRESSION )
. Aside: if a syntax word does
not have an explicit closer, it usually has mandatory parentheses. The return
keyword also has conditional variants: returnif( TEST )( VALUES )
and
returnunless( TEST )( VALUES )
where returnunless TEST
is the same as
returnif( not( TEST ) )....
The quitloop
syntax is much the same as break
in languages such as C/C++/Java/C#/ECMAScript/Python. However you can specify
how many loops are exited by supplying a count. e.g. quitloop(2)
will break
out of 2 enclosing loops and not just 1. And it has variants quitif
and
quitunless
that work as you might expect and also optionally can take a count
of how many loops to break out of. And the nextloop
syntax works in exactly
the same way as quitloop
. It is equivalent to continue
in C-like languages.
It has variants nextif
and nextunless
and also takes an optional number
referring to the enclosing loops. e.g. nextloop(2)
will break from the current
loop and force continuation in the next loop out.
In addition, Pop-11 supports full-lexical goto
. Very few programmers will be
familiar with this and it is hardly ever useful. Look it up if you want extra
grey hairs. (It was actually added to Pop-11 to support Common Lisp.)
Pop-11 has a collection of data types that is ... eclectic.
Booleans are true
and false
. However conditionals are only interested in
false and not-false. So false
plays a special role and true
can be replaced
by any other value.
The short-circuit conditionals and
and or
directly support this. So false
or 3
will evaluate to 3
and not true, for example. This is a pervasive idiom
in Pop-11 (which it inherited from Lisp).
Note that not
is an ordinary function. It turns false
to true
and vice
versa.
Pop-11 has unlimited precision integer and rational arithmetic (much the same as Python). It supports single and double precision floating point and complex arithmetic. (Aside: the rules are identical to those of Common Lisp)
Pop-11 also extensively uses a protocol it calls repeaters. A repeater is a procedure with signature:
<T> () -> (T | termin)
In other words a repeater of type T
is a procedure that each time you call it
returns an object of type T
or the special value termin
. termin
is a
unique, singleton value that is exclusively used to signal the end of a
repeater. (Advanced programmers: termin
is not a first-class value but is
reserved for this use and no other.)
Pop-11 presents all stream based operators in terms of repeaters. For example,
to read from a file F
one calls discin(F)
and gets back a character
repeater.
Lists in Pop-11 are singly-linked lists. This is the workhorse datatype for
collections. Unlike Python/Java lists, there is no list-object that you
push/pull from. Instead they are used declaratively via the fundamental
constructor H::T
, which constructs a new 'pair' with head H
and T
. It is
important to note that T
is typically shared with other 'pairs'.
Being the workhorse datatype, lists have a lot of syntactic complication in
Pop-11. The biggest complication that faces programmers from other languages is
that the list construction syntax [... ]
immediately enters a new syntax
context. Technically this is a quasi-quoted context, where tokens literally
stand for themselves, unless explicitly escaped using ^
and ^^
. It is
important to know that you can switch back to normal expression context using
%
.
So the simplest use of lists is simply
[% 1, 2, 3, 4 %]
All the items that are put on the stack between [
and ]
are gathered up and
turned into a linked chain of pairs. The pair that is returned will have 1
as
the head and [% 2, 3, 4 %]
as the tail - and so on down the chain. The chain
is terminated by a value that counts as the empty list. Although Pop-11 has a
unique value nil
that counts as an empty list, it is not the only way to
represent the empty list. When a dynamic list is exhausted the end of the list
is, in fact, a specially configured pair object. As a consequence, the correct
test for empty lists is null( LIST )
and not LIST == nil
.
Although lists are very heavily used in AI programming, they are not appropriate when random access to members is required because getting the Nth element of a linked list takes N steps. Nor are they appropriate when large data structures are used because each pair consumes 3 long words of store, effectively triple the store that might have been expected.
Pop-11 supplies a 1D array type called a vector. Vectors are compact and fast to
index. They are nearly as widely used as linked lists and have their own syntax
that is very closely based on list syntax. They differ mainly in using { ... }
instead of [ ... ]
. Just like lists, the {
bracket immediately switches to a
quasi-quoted context.
Perhaps shockingly to Python programmers, Pop-11 vectors do not support adding or removing any members. Nor is there a built-in type that does so. Why is this? This is an advanced topic. But the short story is that the need for imperative data structures is much reduced because of the pervasive use of the open-stack. In addition, Pop-11 predates the OO revolution and so the switch away from functional programming never happened.