Graffiticode Core Language Specification

Version: 0.1.1
Date: 2026-01-26

1Introduction

This document defines the Graffiticode Core Language Specification, covering syntax, semantics, and the base library. It excludes dialect-specific constructs, runtime behavior, and extended libraries.

2Lexical Structure

2.1Tokens

  • Identifiers: Alphanumeric symbols beginning with a letter.
  • Numbers: Integers and floats. Negative numbers start with -.
  • Strings: Double-quoted UTF-8 strings.
  • Symbols: (, ), [, ], {, }, :, .., <, >, ,

2.2Comments

  • Line Comments: Begin with | and continue to end of line.

3Syntax

3.1Programs

A Graffiticode program is a sequence of one or more let declarations, followed by a single top-level expression, and terminated with ...

let double = <x: mul 2 x>..
map (double) [1 2 3]..

The top-level expression must always be followed by ...

3.2Expressions

3.2.1Function Application

Function application is written in prefix style: add 1 2

Functions have fixed arity, so applications can be parsed unambiguously without grouping syntax. For example, add 1 mul 2 3 parses as add(1, mul(2, 3)) because the parser knows add takes 2 arguments and mul takes 2 arguments.

Parentheses are used to defer application: map (double) [1 2 3]

3.2.2Lists

[1 2 3]

3.2.3Records

Records may use shorthand syntax for fields where the value is a reference to a variable of the same name.

let foo = 10..
{foo}         | equivalent to {foo: 10}

This provides a concise way to construct records using in-scope variable names as field keys and values.

You can also mix shorthand and explicit fields:

let x = 1..
let y = 2..
{x y z: 3}    | equivalent to {x: 1, y: 2, z: 3}
{ name: "Alice", age: 30 }

3.2.4Tags

A tag value is an arity-0 symbolic value used to represent symbolic variants in pattern matching or other symbolic forms.

Tag values are only defined by using record shorthand syntax. For example:

{red blue green}   | defines the tag values red, blue, and green

Tag values:

  • Must be introduced via record shorthand notation
  • Are resolved as symbolic constants with identity semantics
  • Match directly in case expressions:
let color = get "red" {red blue green}
case color of
  red: "warm"
  blue: "cool"
  _: "other"
end

3.2.5Lambdas

<x: add x 1>

Multiple parameters: <x y: add x y>

3.2.6Let Bindings

let double = <x: mul 2 x>..

3.3Pattern Matching

Pattern matching is done using case:

case x of
  0: "zero"
  1: "one"
  _: "other"
end
Supports
Literalvalues
Tupledestructuring:(a, b)
Recorddestructuring:{ name, age }
Wildcard_

Pattern matching on function arguments is disallowed.

4Type System

Graffiticode includes a implicit structural type system. Every expression has a statically inferred type, and type errors are detected at compile time. Explicit type annotations are not included in the grammar.

4.1Primitive Types

  • number – Represents integers or floating-point numbers.
  • string – Represents UTF-8 strings.
  • bool – Represents Boolean values: true and false.
  • json – Represents any JSON-compatible value (opaque, untyped).
  • any – Used internally to denote an unconstrained type (e.g., during inference).

4.2Composite Types

  • Lists – Written as [T] where T is any type. [string] | list of strings [number] | list of numbers [[bool]] | list of lists of booleans
  • Records – Key-value maps with known keys and types. { name: string, age: number }
  • Tuples – Ordered, fixed-length collections with heterogeneous types. (number, string, bool)

4.3Function Types

Functions are written using Graffiticode lambda signature syntax: <number number: number> | function taking two numbers and returning a number <string: [string]> | function taking a string and returning a list of strings <list record: record> | common signature for structural transformation

Function types are curried by default. That means: <number number: number> is equivalent to a function that returns another function: <number: <number: number>>

4.4Tag Sets and Tag Value Type

Tag values are symbolic constants introduced using record shorthand syntax. They have a default type of tag, and their identity is preserved across bindings.

4.4.1Tag Identity and Type

Tag values are symbolic constants identified solely by their name. The same tag name (e.g., A) refers to the same symbolic value, regardless of where it is defined.

Tag values compare equal by name. For example, A in {A B} and A in {A C} are equal.

4.4.2Tag Sets as Structural Types

Although tag values are globally identified by name, the type system tracks which tags are expected to co-occur.

For example, {A B} and {A C} are different types:

{A B}    | type: { A: tag, B: tag }
{A C}    | type: { A: tag, C: tag }

This allows the type system to constrain the expected context for pattern matching, record shapes, or enum-style uses of tags.

Using tags not part of the expected set results in a type error.

4.4.3Default Type: `tag`

By default, a tag value has the type:

tag

When tags are introduced with:

{A B C}

the resulting type is:

{ A: tag, B: tag, C: tag }

This allows for composition and safe comparison.

4.4.4Example

let colors = {red blue green}..
case red of
  red: "warm"
  blue: "cool"
  _: "other"
end

In this case, red, blue, and green are all of type tag, and case matches by identity.

4.5Example: Annotated Definitions

let inc = <x: add x 1>  | type: <number: number>
let greet = <name: concat ["Hello, " name]>  | type: <string: string>
let zip = <xs ys: zipLists xs ys>  | type: <[T] [U]: [(T, U)]>

(Note: actual Graffiticode does not use type annotations in let bindings; types are inferred.)

5Semantics

5.1Evaluation Model

  • Purely functional: no side effects
  • Strict evaluation: arguments evaluated before function application
  • Immutable data: all values are immutable

Many built-in functions in Graffiticode follow a model-threading pattern. In this pattern, functions are defined to take one or more arguments followed by a model, which represents the current state of the program or view. The function uses the earlier arguments to compute an update to the model and returns a new model as its result.

This style enables a declarative and order-independent composition of functions. Since each function call returns a new model, multiple calls can be reordered without changing the final result, provided the functional dependencies are preserved.

This approach draws inspiration from Model-View-Update (MVU) architectures, in which the model represents the application state and functions describe pure, deterministic transformations of that state.

5.2Functions

  • Fixed arity: every function has a known number of parameters
  • Curried by default: partial application supported

5.3Scoping

  • Lexical scoping
  • Shadowing allowed within nested scopes

5.4Errors

  • Syntax errors: raised during parsing
  • Type errors: raised during compilation
  • Runtime errors: e.g., out-of-bounds access

6Base Library

6.1Types

  • number
  • string
  • bool
  • list
  • record
  • tuple
  • json

6.2Built-in Functions

Function Signature Description
add <number number: number> Adds two numbers
and <bool bool: bool> Logical AND operation
apply <function list: any> Applies a function to a list of arguments
div <number number: number> Divides numbers
equiv <any any: bool> Tests if two values are strictly equivalent
filter <function list: list> Keeps items matching predicate
get <string record: any> Retrieves a value from a record by key
hd <list: any> First item of list
isEmpty <list: bool> Returns true if the list is empty
log <any: any> Logs the value to console and returns it (identity function)
map <function list: list> Applies function to each item
max <number number: number> Returns the larger of two numbers
min <number number: number> Returns the smaller of two numbers
mod <number number: number> Remainder of division
mul <number number: number> Multiplies numbers
not <bool: bool> Logical NOT operation, inverts a boolean value
nth <number list: any> Nth element of list
or <bool bool: bool> Logical OR operation
range <number number number: list> Generates a range list
reduce <function any list: any> Combines list using a reducer with initial value
set <string any record: record> Returns a new record with a key set to a value
sub <number number: number> Subtracts numbers
tl <list: list> All items except first

6.2.1add

Add two numbers.

add 2 3  | returns 5

6.2.2and

Logical AND operation

and false false  | returns false
and false true   | returns false
and true false   | returns false
and true true    | returns true

6.2.3apply

Apply a function to an argument list

apply add [1 2]  | returns 3

6.2.4div

Divide the first number by the second

div 10 2  | returns 5

6.2.5equiv

Tests if two values are strictly equivalent

equiv 1 1        | returns true
equiv "a" "a"    | returns true
equiv true true  | returns true
equiv 1 2        | returns false
equiv "a" "b"    | returns false

6.2.6filter

Filter elements matching predicate

filter (<x: mod x 2>) [1 2 3 4]  | returns [1 3]

6.2.7get

Retrieve a record field

get "b" {a: 1, b: 2}  | returns 2

6.2.8hd

Return the first item

hd [10 20 30]  | returns 10

6.2.9isEmpty

Return true if list is empty, otherwise return false

isEmpty []  | returns true

6.2.10log

Log a value to the console and return the value unchanged

log "Hello"  | prints "Hello" to console and returns "Hello"
log (add 1 2)  | prints 3 to console and returns 3

6.2.11map

Apply a function to each element

map (<x: add x 1>) [1 2 3]  | returns [2 3 4]

6.2.12max

Return the larger of two numbers

max 5 10  | returns 10

6.2.13min

Return the smaller of two numbers

min 5 10  | returns 5

6.2.14mod

Compute the remainder

mod 10 3  | returns 1

6.2.15mul

Multiply two numbers

mul 4 3  | returns 12

6.2.16not

Logical NOT that inverts a boolean value

not true   | returns false
not false  | returns true

6.2.17nth

Get the nth item (0-based)

nth 1 [10 20 30]  | returns 20

6.2.18or

Logical OR operation

or false false  | returns false
or false true   | returns true
or true false   | returns true
or true true    | returns true

6.2.19range

Produce a range list from start to end (exclusive) with step

range 1 10 2  | returns [1 3 5 7 9]

6.2.20reduce

Reduce a list to a single value, starting with an initial value

reduce (<a b: add a b>) 0 [1 2 3 4]  | returns 10

6.2.21set

Return a new record with an updated field

set "a" 2 {a: 1}  | returns {a: 2}

6.2.22sub

Subtract the second number from the first

sub 5 2  | returns 3

6.2.23tl

Return all but the first item

tl [10 20 30]  | returns [20 30]

7Program Examples

let double = <x: mul 2 x>..
map (double) [1 2 3]..
case age of
  18: "adult"
  _: "other"
end..

---

§Index

  1. Supports
  1. 1Introduction
  2. 2Lexical Structure
    1. 2.1Tokens
    2. 2.2Comments
  3. 3Syntax
    1. 3.1Programs
    2. 3.2Expressions
      1. 3.2.1Function Application
      2. 3.2.2Lists
      3. 3.2.3Records
      4. 3.2.4Tags
      5. 3.2.5Lambdas
      6. 3.2.6Let Bindings
    3. 3.3Pattern Matching
  4. 4Type System
    1. 4.1Primitive Types
    2. 4.2Composite Types
    3. 4.3Function Types
    4. 4.4Tag Sets and Tag Value Type
      1. 4.4.1Tag Identity and Type
      2. 4.4.2Tag Sets as Structural Types
      3. 4.4.3Default Type: `tag`
      4. 4.4.4Example
    5. 4.5Example: Annotated Definitions
  5. 5Semantics
    1. 5.1Evaluation Model
    2. 5.2Functions
    3. 5.3Scoping
    4. 5.4Errors
  6. 6Base Library
    1. 6.1Types
    2. 6.2Built-in Functions
      1. 6.2.1add
      2. 6.2.2and
      3. 6.2.3apply
      4. 6.2.4div
      5. 6.2.5equiv
      6. 6.2.6filter
      7. 6.2.7get
      8. 6.2.8hd
      9. 6.2.9isEmpty
      10. 6.2.10log
      11. 6.2.11map
      12. 6.2.12max
      13. 6.2.13min
      14. 6.2.14mod
      15. 6.2.15mul
      16. 6.2.16not
      17. 6.2.17nth
      18. 6.2.18or
      19. 6.2.19range
      20. 6.2.20reduce
      21. 6.2.21set
      22. 6.2.22sub
      23. 6.2.23tl
  7. 7Program Examples
  8. §Index