←THE LAST ACTION LANGUAGE→
←THE_LAST_ACTION_LANGUAGE→
overview
- natural-language agnostic (all keywords are 1 special character) so you can program in Toki Pona
- prefix notation
- variadic functions are not allowed
- tacit programming
- variables are mutable
- their types are immutable
- functions are values
- events
types
there are 8 types
void [;] type [~] error [`] boolean [?] number [0] block [_] function [#] event handler [:]
basics
the syntax is all prefix notation, so to assign (2+5)*3 to a variable Foo, you would
$ [Foo] * + 2 5 3
all functions have constant arity, so you always know which argument goes to which parameter. in the example above, we use the function $ which is the assignment. assignment needs 2 arguments, the first is the name of a variable, and the second is a value. let's go step by step.
; [ this is a comment ] $ ; [ this is our assignment function call ] [Foo] ; [ this is the first argument of $ ] * + 2 5 3 ; [ this is the second argument of $ ]
again, slower.
$ ; [ this is our assignment function call ] [Foo] ; [ this is the first argument of $ ] * ; [ this is the second argument of $ ] + ; [ this is the first argument of * ] 2 ; [ this is the first argument of + ] 5 ; [ this is the second argument of + ] 3 ; [ this is the second argument of * ]
notice how Foo is between square brackets. this is because assignment is not a special form, so Foo alone would have been evaluated and 21 would have been assigned not to Foo but to Foo's current value.
btw, using a variable that has not been assigned a value yet, raises an error. you need to initialize them before you use them.
immutable types
once a variable has a value, its type becomes immutable.
$ [Foo] 5 $ [Foo] [bar baz]
this raises an error because 5 is a number while [bar baz] is a block. this would change the type of Foo, which is forbidden.
section
the source code is made of sections. here is a section named Init.
§ [Init]
the content of a section is accessible by using its name like a variable name. you can read it, and you can also assign a new value to it, which changes directly the source code of the running program.
$ [Init] [ § [Init] some code here...]
conditional
if-then-else goes like:
? ; [ if ] < n 0 ; [ the condition, directly ] [ $ [n] 0 ] ; [ then-part code, enclosed in a quoting block ] [ ] ; [ else-part is mandatory, even though it's empty ]
then-part and else-part must be enclosed in blocks to prevent their direct execution.
loooop
who needs loops anyway? ok, let's calculate
$ [n] p $ [r] 1 € < 0 n ; [ condition ] [ $ [n] - n 1 $ [r] * 2 r ] ; [ loop body ]
index
say you have a list.
$ [list] [ One A Two B Three C 0 D 1 E 2 F ]
then after
$ [x] ° list 0 $ [y] ° list 1 $ [z] ° list [Two]
x is One, y is A, and z is B.
if the index is a number, then the list is treated as an array. if the index is a block, the list is treated as a key-value object. if the index is something else, an error is raised.
template
inside of a block, everything between curlies will be evaluated, and the curlies will be replaced by the result of the evaluation. no matter how deep curlies are.
[ this is a [ test where one plus one equals { + 1 1 } ] ]
will become
[ this is a [ test where one plus one equals 2 ] ]
function assembler
the function assembler is not a special form. it is a function which needs 3 arguments: an arity, a return type, and a function code body.
$ [Double] # 1 [0] [ ^ * 2 @ 1 ] $ [Addten] # 1 [0] [ ^ + @ 1 10 ]
step by step
$ ; [ we asssign ] [Double] ; [ to the variable named "Double" ] # ; [ a function ] 1 ; [ which takes 1 argument ] [0] ; [ and which returns a number ] [ ^ ; [ return ] * 2 ; [ twice ] @ 1 ; [ the first argument ] ]
function disassembler
the function disassembler does the opposite.
$ [Src] ' [Double]
now Src is a block which contains:
# 1 [0] [ ^ * 2 @ 1 ]
evaluation & composition
parens ( ) evaluate their content. say you have
$ [Doubten] [Double Addten]
then if you do
$ [Answer] (Doubten) 11
Answer will be 42.
exception
you can do classical try/catch blocks like this.
" [ + 1 [meh] ] ; [ try block, executed first ] [ oooops... ] ; [ catch block, executed if the try block failed ]
you can also raise an error manually. since I don't like backticks much, the raise function is `.
` [ my own error ]
events, timing & I/O
you can attach an event handler, which returns an handle. with this handle, you can detach the event handler.
events are how you communicate with anything outside of the interpreter. output is done by emitting events, input is received as events.
they also have a timing functionality: when you emit events, you can choose to delay their emission.
$ [keypressedHandle] : [ keypressed ] [ $ [pressedKey] @ 1 handleInput pressedKey ] ; [ now we have an active event handler ] . keypressedHandle ; [ now then handler is deactivated ] > ; [ emit ] 0 ; [ after 0 msec ] [print] ; [ an event named "print" ] [be bop] ; [ with an argument "be bop" ]
shadowing
when you're inside of a closure, if you read a variable, you're reading locally first, then in the callers.
But when you set a variable's value, you're always working on a local variable, even when a variable with the same name exists in the callers.
misc.
to get a list of all variable names:
_
and to get the type of something:
~ [datum]
you must put your datum inside of a block.
keywords
section
syntax:
§ name:block
escape
syntax:
\
conditional
syntax:
? condition:boolean then:block else:block
while
syntax:
€ condition:boolean body:block
index
syntax:
° data:block index:any
block
syntax:
[ ]
evaluate
syntax:
( )
template
syntax:
[ { } ]
set variable value
syntax:
$ varName:block value:any
assemble function
syntax:
# arity:number type:type body:block
return
syntax:
^ value:any
argument
syntax:
@ index:number
disassemble function
syntax:
' funcExpr:block
exception
syntax:
" try:block catch:block
raise
syntax:
` error:block
event on
syntax:
: event:block body:block
event off
syntax:
. handle:event-handler
emit
syntax:
> delay:number event:block argument:any
vocabulary
syntax:
_
no op
syntax:
; comment:any
type of
syntax:
~ datumExpr:block
math
syntax:
+ a:number b:number ; [ add ] - a:number b:number ; [ subtract ] * a:number b:number ; [ multiply ] / a:number b:number ; [ divide ] % a:number b:number ; [ modulo ] , a:number ; [ trunc ] = a:number b:number ; [ equal ] < a:number b:number ; [ less than ] ! a:boolean ; [ not ] & a:boolean b:boolean ; [ and ] | a:boolean b:boolean ; [ or ]
factorial example
$ [factorial] # 1 [0] [ ^ ? < @ 1 2 1 * @ 1 factorial - @ 1 1 ]
length example
$ [length] # 1 [0] [ $ [len] 0 € ! = ° @ 1 len [] [ $ [len] + len 1 ] ^ len ]