Graffiticode Core Language Specification

Version: 0.1.1
Date: 2025-07-07

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

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

3.2.2Lists

[1 2 3]

3.2.3Records

{ name: "Alice", age: 30 }

3.2.4Lambdas

<x: add x 1>

Multiple parameters: <x y: add x y>

3.2.5Let 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.

3.3.1Tag Values

A tag value is an arity-0 symbolic value that can be used in pattern matching or to encode variant types.

red

Tag values:

  • Are unbound identifiers that appear in expression position.
  • May optionally be introduced via implicit enum definitions.
  • Match directly in case expressions:
case color of
  red: "warm"
  blue: "cool"
  _: "other"
end

Tags are resolved as special constants with symbolic identity. They are case-sensitive and may be compared for equality using regular pattern match semantics.

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.2Non-Nullable Types

Graffiticode types are nullable by default. To indicate that a value must not be null, append an exclamation mark (!) to the type name.

string!     | a non-nullable string
[number]!   | a non-nullable list of numbers

A non-nullable type is guaranteed to hold a value. Nullable types may be null or missing.

In general:

  • T allows null
  • T! disallows null

Type inference will propagate non-nullability when safe.

4.3Composite Types

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

4.4Function Types

Functions are written using Graffiticode lambda signature syntax: gc <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: gc <number number: number> is equivalent to a function that returns another function: gc <number: <number: number>>

4.5Tag Value Type

Tag values are symbolic constants with no arguments or payload. They are considered members of an implicit tag set, and their type is usually inferred based on the context in which they appear—such as in pattern matching.

4.5.1Default Type: `tag`

By default, a tag value has the type:

tag

If your program uses a fixed set of tags in a certain context (e.g., red, green, blue), you can think of the type of those values as a discriminated union:

tag ∈ { red, green, blue }

However, Graffiticode does not currently support enumerating or defining tag types explicitly. Instead:

  • All tags are treated as having type tag.
  • They are comparable for equality using case and pattern matching.
  • Their type can unify with record fields, parameters, or variants if used consistently.

4.5.2Example

case color of
  red: "warm"
  blue: "cool"
  _: "other"
end

In this case, color is assumed to be of type tag.

4.6Example: 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.) (for illustration)

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.4Lambdas
      5. 3.2.5Let Bindings
    3. 3.3Pattern Matching
      1. 3.3.1Tag Values
  4. 4Type System
    1. 4.1Primitive Types
    2. 4.2Non-Nullable Types
    3. 4.3Composite Types
    4. 4.4Function Types
    5. 4.5Tag Value Type
      1. 4.5.1Default Type: `tag`
      2. 4.5.2Example
    6. 4.6Example: 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