XXIIVV

Anonymous Functions in Uxntal

In the context of Uxntal, lambdas are unlabeled inline routines delimited by curlies, not unlike Postscript's anonymous procedures. A lambda block is jumped over, and a pointer to the start of the lambda is pushed to the top of the return stack. The body of the lambda can be unquoted with the STH2r and JSR2 opcodes.

#12 #34 { ADD JMP2r } STH2r JSR2

A lambda block is managed label definition which can be used with standard immediate opcodes. For example, to skip over a length of code:

.button LDZ #00 EQU ?{ will-not-eval }

A lambda can be used to store data inline, and ensure that the content is not inadvertenly run.

{ "hello } STH2r print-lambda

Under the hood, the lambda block writes the length to jump by, and that length can be read to get the length of the lambda block.

@print-lambda ( {str}* -- )
	DUP2k #0002 SUB2 LDA2 ADD2 SWP2
	&l ( -- )
		LDAk .Console/write DEO
		INC2 GTH2k ?&l
	POP2 POP2 JMP2r

A higher-order function is a function that takes a function as an argument or returns one as a result. In the following example, the foreach routine is expecting a pointer to a series of bytes, and a pointer to a function to apply on each byte-long item in memory.

{ 01 02 03 04 05 } STH2r ;double foreach

The body of the double function reads the value of a cell in memory and writes a result equal to twice its value, and the body of the foreach function is merely applying a function to each cell in memory.

@double ( addr* -- addr* )
	STH2k LDAk
	DUP ADD
	STH2r STA
	JMP2r

@foreach ( {bytes}* fn* -- bytes* )
	,&t STR2
	DUP2k #0002 SUB2 LDA2 ADD2 SWP2
	&l ( -- )
		[ LIT2 &t $2 ] JSR2 INC2 GTH2k ?&l
	POP2 POP2 JMP2r

Consider that the whole double function can equally be inlined:

{ 01 02 03 04 05 } STH2r { STH2k LDAk DUP ADD STH2r STA JMP2r } STH2r foreach

As another demonstration of the same concept, we can inline a list of items, here's an implementation of Lisp's member function that returns the member in a list, or nil.

{ =cat =dog =bat } STH2r ;rat member
@member ( {items}* target* -- res/-1* )
	,&t STR2
	DUP2k #0002 SUB2 LDA2 ADD2 SWP2
	&l ( -- )
		LDA2k [ LIT2 &t $2 ] EQU2 ?&found
		INC2 INC2 GTH2k ?&l
	POP2 ;nil &found NIP2 JMP2r

Lambdas can also be nested into one another, but remember, only the outermost layer of a nested lambda is evaluated at a time:

#01 { { "foo $1 } STH2r !print-lambda } STH2r JCN2

incoming uxntal syntax uxntal immediate drifblim