>>92
Like Common Lisp assignment, Symta's `=` is not just for variables,
but can reach inside structures. So you can use it to modify
associative lists or array indices:
-> = X:ø = (X.abc.def = 123) = X
((abc ((def 123))))
-> = X:[1 2 3] = (X,1 = 123) = X
Note that `=` is non-destructive, because it creates a new list on
each use, instead of modifying underlaying structure.
Lists are useful in exploratory programming because they're so
flexible. You don't have to commit in advance to exactly what a
list represents. For example, you can use a list of two numbers
to represent a point on a plane. Some would think it more proper
to define a point object with two fields, x and y. But if you use
lists to represent points, then when you expand your program to
deal with n dimensions, all you have to do is make the new code
default to zero for missing coordinates, and any remaining planar
code will continue to work.
Or if you decide to expand in another direction and allow partially
evaluated points, you can start using symbols representing variables
as components of points, and once again, all the existing code will
continue to work.
In exploratory programming, it's as important to avoid premature
specification as premature optimization.
The most exciting thing lists can represent is code. The lists you
build with [] and \() are the same things programs are made out of.
This means you can write programs that write programs. The usual
way to do this is with something called a macro. We'll get to those
later. First, functions.
We've already seen some functions: `+`, lhd, ltl, and t. You can
define new ones with `=`, which takes a symbol to use as the name,
a list of expressions representing the parameters, and then zero or
more `=` prefixed expressions called the body. When the function
is called, those expressions will be evaluated in order with the
symbols in the body temporarily set ("bound") to the corresponding
parameters. Whatever the last expression returns will be returned as
the value of the call.
Here's a function that takes two numbers and returns their average:
-> average X Y = (X+Y)/2
$(fn Name:average Adr:#x1002DB0EEB)
-> average 2 4
3
The body of the function consists of one expression, "= (X+Y)/2".
It's common for functions to consist of one expression; in purely
functional code (code with no side-effects) they always do.
Notice that without `=`, `average X Y` would be just a function call.
What's the strange-looking object returned as the value after defintion
expression? That's what a function looks like. In Symta, as in most
Lisps, functions are a data type, just like numbers or strings.
As the literal representation of a string is a series of substrings
surrounded by double quotes, the literal representation of a function
is a list enclose inside `<>` braces. So you could represent an
unnamed (anonymous) function to return the average of two numbers as:
-> <X Y = (X+Y)/2>
$(fn Name:r Adr:#x1002E4432B)
There's nothing semantically special about named functions as there
is in some other languages. All `average X Y = (X+Y)/2` just assigns
<X Y = (X+Y)/2> to the symbol `average`.
And of course you can use a literal function wherever you could use
a symbol whose value is one, e.g.
-> <X Y = (X+Y)/2> 2 4
3
This expression has three elements, <X Y = (X+Y)/2>, which
yields a function that returns averages, and the numbers 2 and 4.
So when you evaluate all three expressions and pass the values of
the second and third to the value of the first, you pass 2 and 4
to a function that returns averages, and the result is 3.
Careful reader should note, that we omit parentheses around top-level
expressions. This notation has it tradeoffs, because now we have to
explicitly call any function, which takes no arguments. We use `c`
function for this:
-> <= 1 + 2>
$(fn Name:r Adr:#x100311C1BB)
-> c <= 1 + 2>
3
-> c <X = 1 + 2 + X> 4
7
As you can see, `c` can also call a function, which does take arguments.
Basically, `c` is analogous to funcall in Common Lisp.
There's one thing you can't do with functions that you can do with
data types like symbols and strings: you can't print them out in a
way that could be read back in. The reason is that the function
could be a closure; displaying closures is a tricky problem.
In Symta, functions can also parse its arguments, akin to BNF. So
we can destructure list with them or just do pattern matching:
-> <[X Y] = (X+Y)/2> [2 4]
3
-> <[X Y] = (X+Y)/2> [2 4 6]
ø
-> <[X:even? Y] = (X+Y)/2> [2 4]
3
-> <[X:even? Y] = (X+Y)/2> [3 4]
ø
Note how <[X Y] = (X+Y)/2> returns ø (nil or false), when argument
doesn't match pattern. Many would consider such default unsafe, but
it greatly reduces code size. [X:even? Y] matches only pairs with
even first element: `even?` is just a predicate function. Symta's
predicates are usual functions, but have semantics of returning their
arguments, when it matches condition, or ø. For convenience predicate
names end with `?`. `X:even?` binds result of `even?` to X, or
returns ø, when `even?` returns ø.
The operator `:` can also be used for establishing temporary
variables, when used after `=`:
-> <X Y = Z:X+Y = Z/2> 2 4
3
-> = X:2 = X+X*2
6
-> = X:3 = Y:4 = sqrt X^2 + Y^2
5.0
operator `^` does exponentiation.
So far we've only had things printed out implicity as a result of
evaluating them. The standard way to print things out in the middle
of evaluation is with `p` or `say`. The difference is that `p` does
pretty printing and returns it's argument, while `say` returns ø.
`p` is useful for debugging, as you can easily insert `p` between
function calls:
-> <X Y = (X+Y),p/2> 2 4
6
3
-> average X Y = say “my arguments were: $X $Y” = (X+Y)/2
$(fn Name:average Adr:#x1004DCA31B)
-> average 100 200
my arguments were: 100 200
150
For the first time we used `,` operator in it's other role - applying
function to an argument:
-> 3,<X=X*2>,<X=X/5>
6/5
The standard conditional operator is {}. It acts both as if and
cond/switch statement:
-> {odd? 1 = \a; \b}
a
-> {odd? 2 = \a; \b}
b
-> yoba A B = {A≤B = “A<B”
;A≥B = “A>B”
; √ = “A=B”}
$(fn Name:yoba Adr:#x10033CA54B)
-> yoba 7 4
`A>B`
-> yoba 2 4
`A<B`
When `=` is missing it {} just return the first true value:
-> {even? 3; odd? 7; 123}
7
-> {even? 3; odd? 8; 123}
123