First off, Lispers will be happy to learn that Logix has both code quoting and a procedural macro system. Logix programs are not made from lists, they are made from user-defined object types. More on this in 7 - Languages: Extending and Creating.
The Logix prompt can recognize multiple languages. It initially expects Standard Logix (std). Simple function calls in Standard Logix look a lot like Lisp.
[std]: max 4 (min 5 6)
5
Note parentheses are not required on the outer function. In fact, parentheses do not have the special status they do in Lisp – they are just a scoping operator as in most programming languages. A function-call expression is simply a sequence of expressions:
func arg1 arg2 ... argn
Logix handles first-class functions as does Python – i.e. it is Scheme-like not Lisp-like. The astute reader may be wondering how to call a zero argument function. E.g.
[std]: from random import random
[std]: print random
<built-in method random of Random object at 0x009B0368>
The function was not called. To call it, use a special postfix operator ().
[std]: print random()
0.746969538583
Note that () is a special operator. It is not empty parentheses. (in case you’re wondering, the empty list in Standard Logix is [])
As well as the function call syntax, Standard Logix has many operators with a rich syntax.
[std]: x = 1 + 2
[std]: print if x == 3: 'good' else: 'oh dear'
'good'
[std]: aTuple = 1, 2, 3, 4
[std]: [x * 2 for x in aTuple]
[2, 4, 6, 8]
Blocks are delimited by indentation:
[std]: for x in range 10:
: stars = "*" * x
: print stars
There are a couple of other tricks done with white-space. More later.
Identifiers in Logix follow C like lexical rules. However, any character can be used when extending the syntax, i.e. when defining new operators (see 7 - Languages: Extending and Creating).
Logix is implemented as a font-end to Python, so a good deal of Python semantics are exposed. Lisp folks need to watch out for a few things. One way to go would be to read up on Python (docs.python.org), but for the impatient, here’s a few highlights:
In Logix, # is the comment character. To write a literal symbol in Standard Logix, use ~
[std]: ~foo
~foo
Note this operator has another trick up its sleeve:
[std]: ~("foo".upper() + "!")
~FOO!
Logix supports the back-quote operator just like Lisp:
[std]: `max 4 5
<std: max 4 5>
Unlike Lisp, the result is not a list, but an operator object. In this example, the operator represents a function call.
We can also quote operators:
[std]: `1 + 2
<std:+ 1 2>
The returned value is an instance of the + operator from the language std, with operands 1 2, it reads as a prefix notation of the expression.
Inside the quote, the backslash can be used like the comma in Lisp:
[std]: var = ~x
[std]: ` \var += 1
<base:+= x 1>
(note that the += operator is inherited from another language: Base Logix)
Multiple backslashes can be used to escape nested quotes just as with multiple commas in Lisp.
The \* operator is the equivalent of Lisp’s ,@ i.e. it splices a list into the quoted value.
[std]: l = [3, 45, 21]
[std]: `max \*l
<std: max 3 45 21>
You can evaluate these quoted expressions using logix.eval
[std]: logix.eval `max \*l
45
Defining macros is performed hand-in-hand with defining new operators with custom syntax. This is covered in more depth in 7 - Languages: Extending and Creating. By way of a quick example:
[std]: defop 50 expr "++" macro ex: `\ex += 1
Here we have defined a new postfix operator with a binding value of 50, implemented as a macro. The macro function takes a single argument (ex) and returns the expanded code template.
[std]: x = 1
[std]: x++
[std]: x
2
You can experiment and debug using logix.macroexpand and logix.macroexpand1.
[std]: logix.macroexpand `a++
<base:+= a 1>
[std]: logix.macroexpand `player.score++
<base:+= (base:. player score) 1>