Saturday, March 03, 2007

SubX. Common Lisp Sub-eXpression language.

There are some disadvantages to using common lisp. Some of them I've mentioned in my previous post. Subx tries to improve on these or possibly eliminate some of them.
A grammar that is intuitive enough and covers the usual programming patterns isn't impossible to create. It has been and will continue to be developed in future languages. Macros allow you to extend the existing grammar at various levels. This is the case with the 'loop' macro that defines it's own iteration-language. This is one of the most complex that is offered by the standard implementation of common lisp. There are others of course like 'dotimes', 'dolist' etc. Subx tries to offer structures that are general enough to apply to usual cases and simplify overall the code. It provides a framework for creating grammars. It's based only on a few very basic concepts: operators: unary, binary and parentheses: associativity, context change parens and proper lisp parens. Using these concepts custom macros/functions can be used with a more clear syntax.

Expressions that use the subx-grammar can be used in lisp by surrounding them in []. These are just read as normal paranthesized expressions so you can use them anywhere:

(defun some-function()
[ <subx-expression> ])

(loop for x below 10 collect
[ <subx-expression> ])

Well, you get the idea.
Now, just to start on the safe side subx can be inserted in normal lisp code but also normal lisp code can seamlessly be integrated in subx. So [] can contain (<symbo|form>*) that are interpreted as lisp expressions (s-expr). So writing something like [(list 1 2)] is pretty much the same as (list 1 2) and also [(list 1 [2])] or some other nesting.

As you guessed () act as normal parens. So [(1)] isn't that legal cause (1) is not.

The third kind of parens used are {}. These represent function application. The syntax is function{<param,>*<param>}
Operators have associativity and priorities. Some are already defined: common arithmetic, some logic. Any othe operatator is considered a function.

There are a couple of operators that I've defined already. These include:
- sequencing by progn: ';'
- sequencing by forms enumeration: ':'
- list construction: ','
- function call on a form: '.'
- function call '{}'
- apply function '~'
- map '<-', mapcan '<<-', filter '<->', reduce '<=>'
- arithmetic, logical, bitwise
- assignment: '=', destructuring bind: ':='

Some basic examples:
Writing a function call:

(list 1 2)
[list 1 2]
[list{1,2}]
[apply #'list 1,2]
[funcall #'list 1 2]
[1.(list 2)]
[1.list~2]
[1.list{2}]
==> (1 2)

Applying a map:

[1..5 <- ((x) (inc x))]
[1..5 <- inc]
[1..5 <- #'inc]
[1..5 <- (lambda (x) (inc x))]
==> (2 3 4 5 6)

Filter:

[1..10 <-> oddp]
==> (1 3 5 7 9)

Reduce:

[1..10 <=> #'+]
==> 55

Ifs:

[if 1 + 2 == 3: list{1,2}: list~3]
==> (1 2)

[when 1 == 1: print 1,2,3]
==> (1 2 3)

[unless 1 == 2: print{1,2,3}]
==> 1 2 3

Nesting ifs:

[if 1 == 1:
[if 2 == 1 + 2: print{2}: print{3}]:
print{4}]

Weird add-ing:

[+ 1.+{2}.+~3.(+ 4) + 5 6]
==> 21

Weird equals:

3 == 1 + 2 == 4 - 1
==> 3

Slicing:

[..{10}@..{3}]
[[..10]@[..3]]
[(..10)@(..3)]
[..~10@..~3]
==> (0 1 2)

Loop-ing:

[for x on 1..5 <- list collect:
sum{x@[..2] <- car}] ==> (3 5 7 9 5)

Using macros:

[let ((x 9)): x = print{x + 1} + 2; x,2]
==> 10 10
==> (12 2)

[for x in 1..10 collect: x + 1]
==> (2 3 4 5 6 7 8 9 10 11)

[x = 0; while [x += 1] < 10 collect: x,]
==> ((1) (2) (3) (4) (5) (6) (7) (8) (9))

[defparameter x: 1 + 2, 4]
x
==> (3 4)

[(x &rest y) := [..5]; x, y]
==> (0 (1 2 3 4))

[f = [lambda (x): x + 1]; f{1}]
==> 2

Thursday, March 01, 2007

What about Lisp?

What about this oldy, Common Lisp? Great language overall. Some glitches here and there.

Skipping the dispute about strictly functional vs tolerable imperative, lisp is a great language for expressing yourself in the most intimate way allowed by a programming language. Using programming patterns makes a good programmer, but creating your own also makes a happy programmer.

There's a permanent need in lisp to write code in an awkward manner. This sometimes leads to frustration but most often is compensated by the sheer joy of being able to express everything as it comes to mind and not having to convert things to a preexisting grammar-enforced pattern of thought.
The main reasons for frustration that are frequently mentioned are:

1. parentheses - There's too many of them. Even with parens-match highlighting or other nifty tricks like list depth color-codes the lines blurr until you exercise your focus skill.
2. prefix notation - It's hard to get used to this. The normal pattern of thought is to apply transformations on lists from left to right. Sometimes even after gaining some experience with the prefix form (when you start feeling good about writing parens) you still think in left to right transformations so you actually do a manual conversion from left to right to prefix notation.
3. notations for symbol list creation - They get very confusing. And it's not just about the level of indirection (symbol to value stuff). When you start using macros the first thing you learn is about the back-tick operator and it's associated ',' expression evaluation. This is just crazy when used recursively.
4. list construction - When constructing lists that are not quoted you must specify every time a list construction function like list or maybe use an abbreviation like '@' (@ x 2 (@ y 5))

Any programming language that has a decent grammar (Haskell, Python, Ruby) doesn't have these problems but neither does it have the power inherent to lisp. Some of those things that make lisp better:

1. Expressions can be inserted almost everywhere in an existing code. This is why ideas can be expressed straight-forward as they come to mind.
2. Macros provide compile time AST parsing and restructuring.
3. Macros can also be considered to provide compile time lazy evaluation of expressions.
4. Uniform syntax for any kind of function/operator (the prefix notation).
5. Quoting allows having direct access to symbols. You are not forced to use strings, can manipulate literals in the code directly.

There are many others but let's just not get carried away. All these said lisp is great for being happy with yourself but not really caring about working with others. The sparseness of ready to use modules/packages creates a barrier for the beginner, too varied coding patterns creates a barrier for a possible community. Too many barriers are discouraging to brake. Those that do succeed create a "group" of solitary individuals and do not try to brake the final barrier: share what you create.