An Erlang implementation of the Nock interpreter
Nock is a minimalist, deterministic, combinatorial language based on functions and expressions. This Erlang implementation provides the same functionality as the Ruby version using Erlang's pattern matching and functional programming features.
In Erlang, Nouns are represented as:
- Atoms: Plain integers (e.g.,
42) - Cells: 2-tuples
{Head, Tail}(e.g.,{50, 51})
Core Noun utilities:
from_list/1- Convert Erlang list to Noun representationto_list/1- Convert Noun back to list representationat/2- Tree addressing (implements the/operator)is_atom/1,is_cell/1- Type predicatesincrement/1- Increment an atomequal/2- Equality check
The Nock interpreter:
parse/1- Parse string notation into Noun (e.g., "[[50 51] [0 1]]")interpret/1- Execute Nock formulanock/1- Convenience function to parse and interpret
*[a 0 b] -> /[b a]
Access element at position b in subject a.
Example:
nock:nock("[[50 51] [0 2]]"). % Returns 50*[a 1 b] -> b
Return constant b regardless of subject.
Example:
nock:nock("[[20 30] [1 67]]"). % Returns 67*[a 4 b] -> +*[a b]
Increment the result of interpreting [a b].
Example:
nock:nock("[50 [4 [0 1]]]"). % Returns 51mkdir -p ebin
erlc -o ebin src/*.erlThis will:
- Compile all src/*.erl files
- Compile all test/*.erl files
- Run all discovered EUnit tests
- Show results
rebar3 eunitTo verify what tests it finds:
rebar3 eunit --verboseTo run specific test modules:
rebar3 eunit --module=test_nock_0
rebar3 eunit --module=test_nock_0,test_nock_1erl -pa ebinThen in the Erlang shell:
1> nock:nock("[[50 51] [0 1]]").
Interpreting [[50 51] [0 1]] as Nock...
=> {50,51}
{50,51}
2> nock:nock("[[20 30] [1 67]]").
Interpreting [[20 30] [1 67]] as Nock...
=> 67
67
3> nock:nock("[50 [4 [0 1]]]").
Interpreting [50 [4 [0 1]]] as Nock...
=> 51
51The Erlang implementation follows a functional approach:
- Pure functions for all operations
- Pattern matching for type discrimination
- Recursive tree traversal for addressing
- Tagged tuples for data representation
This makes the code naturally suited to Erlang's strengths while maintaining semantic compatibility with the Ruby version.
The at/2 function implements Nock's tree addressing:
/[1 a]returnsa(identity)/[2 [a b]]returnsa(head)/[3 [a b]]returnsb(tail)- For indices > 3, binary representation is used to navigate the tree
Example: /[6 [[a b] [c d]]] -> c
- 6 in binary is
110 - Remove leading 1:
10 1= go right (tail),0= go left (head)- Result:
c
Additional Nock opcodes can be added to interpret_opcode/3 in nock.erl:
- Nock 2: Distribution
- Nock 3: Cell test
- Nock 5: Equality test
- Nock 6-10: Additional operators
rebar3 eunit