Language Reference

Phograph is a visual, object-oriented, dataflow programming language descended from Prograph. This reference covers its core concepts, data types, primitives, and control flow.

Contents

Dataflow Model

Data flows from top to bottom through a directed acyclic graph of operation nodes. A node executes when all its input data is available (data-driven firing rule). Execution order depends solely on data dependencies, not spatial position.

Phograph has two kinds of wires:

A node with an execution-in pin fires only after the upstream execution wire has triggered, even if all data inputs are already available. Execution wires are optional — pure-dataflow graphs need none.

Program Structure

A Project contains one or more Sections. Each section has three compartments:

Methods and Cases

A method has one or more cases. Each case is a separate dataflow graph with an input bar (parameters) and an output bar (return values). When a method is called, case 1 runs first. If a control annotation triggers “next case,” case 2 runs, and so on.

Every method has a defined arity — a fixed number of inputs and outputs. Inputs can be marked optional (with a default value) or variadic (collects into a list).

Data Types

Phograph has thirteen data types:

TypeDescription
Integer64-bit signed whole numbers
Real64-bit IEEE 754 floating point
StringUTF-8 text
Booleantrue / false
ListOrdered, mixed-type collection (1-indexed)
DictKey-value map (keys must be hashable)
DataRaw binary byte buffer
DatePoint in time with nanosecond precision
ObjectInstance of a user-defined class
ExternalOpaque reference to a platform resource
ErrorError with message, code, and details
EnumEnum value with optional associated data
NullThe single “nothing” value

Phograph is dynamically typed. Types are checked at runtime. Optional type annotations can be added to pins for documentation and validation.

Lists are 1-indexed (the first element is at position 1), following the original Prograph convention.

Node Types

TypeAppearanceDescription
PrimitiveRectangle with bottom lineBuilt-in operation
Universal methodRectangle with nameCall to a standalone method
Local methodRectangle with side linesEmbedded method body evaluated inline within the parent case
Instance generatorOctagonCreates a new object
ConstantHorizontal line with valueSupplies a fixed value
GetSpecial iconReads an attribute from an object
SetSpecial iconWrites an attribute on an object
PersistentOvalStores/retrieves values that persist across method invocations
EvaluationRectangle with expressionInline expression evaluation (arithmetic, boolean, ternary)
InjectRectangle with inject pinDynamic dispatch by name string or method-ref
LoopRectangle with loop iconIteratively calls a method, feeding outputs back as inputs

Pin Types

Nodes have input pins at the top and output pins at the bottom. Pin shapes indicate type compatibility: circles for scalars, hexagons for booleans, squares for collections, diamonds for optionals, pentagons for objects, triangles for execution flow.

Pins have additional properties:

Wire Types

Node Annotations

In addition to control-flow annotations (see Control Flow), nodes support structural annotations that change how they process data:

AnnotationDescription
ListMapApplies the node operation to each element of a list input, producing a list of results
PartitionSplits a list into two lists (pass/fail) based on a boolean result from the node
TryCatches errors on a designated output pin instead of propagating failure

Pattern Matching Guards

Cases in a method support pattern matching guards that control which case fires based on the input values. Guard types:

Primitives Reference

Arithmetic

PrimitiveInputsOutputDescription
+a, bsumAddition
-a, bdifferenceSubtraction
*a, bproductMultiplication
/a, bquotientDivision
moda, bremainderModulo
absn|n|Absolute value
roundnroundedRound to nearest integer
floornfloorFloor
ceilingnceilingCeiling
sqrtnrootSquare root
powerbase, expresultExponentiation
mina, bminimumMinimum of two
maxa, bmaximumMaximum of two
clampval, lo, hiclampedClamp to range
pi3.14…Pi constant
sinradsineSine
cosradcosineCosine
tanradtangentTangent
lnnlogNatural logarithm
log10nlogBase-10 logarithm
randrealRandom number [0, 1)

Comparison

PrimitiveInputsOutputDescription
=a, bbooleanEquality
!=a, bbooleanNot equal
<a, bbooleanLess than
>a, bbooleanGreater than
<=a, bbooleanLess than or equal
>=a, bbooleanGreater than or equal

Logic

PrimitiveInputsOutputDescription
anda, bbooleanLogical AND
ora, bbooleanLogical OR
notabooleanLogical NOT

String

PrimitiveInputsOutputDescription
concata, bstringJoin two strings
lengthstringintegerNumber of characters
to-stringanystringConvert to text
splitstring, seplistSplit by separator
trimstringstringStrip whitespace
replacestr, old, newstringReplace all occurrences
uppercasestringstringConvert to uppercase
lowercasestringstringConvert to lowercase
string-contains?str, subbooleanTest if contains substring
string-starts-with?str, prefixbooleanTest prefix
string-ends-with?str, suffixbooleanTest suffix
char-atstr, indexstringCharacter at position (1-indexed)
string-repeatstring, countstringRepeat string N times

List

PrimitiveInputsOutputDescription
get-nthlist, indexelementGet element (1-indexed)
set-nthlist, idx, vallistSet element (returns new list)
appendlist, elementlistAdd element to end
lengthlistintegerNumber of elements
firstlistelementFirst element
restlistlistAll but first element
sortlistlistSort ascending
reverselistlistReverse order
empty?listbooleanTrue if empty
contains?list, elembooleanTest membership
concatlist1, list2listConcatenate two lists
ziplist1, list2listCombine element-wise into list of pairs
uniquelistlistRemove duplicates
enumeratelistlistList of [index, value] pairs (1-indexed)

Higher-Order List

These primitives take a method-ref as a callback argument (see Method References).

PrimitiveInputsOutputDescription
maplist, method-reflistApply function to each element
filterlist, method-reflistKeep elements where callback returns true
reducelist, method-ref, initialvalueFold list with accumulator
flat-maplist, method-reflistMap then flatten one level
any?list, method-refbooleanTrue if callback returns true for any element
all?list, method-refbooleanTrue if callback returns true for all elements
findlist, method-refvalueFirst element where callback returns true (null if none)
sort-bylist, method-reflistSort by key extracted by callback
group-bylist, method-refdictGroup elements by key extracted by callback

Dict

PrimitiveInputsOutputDescription
dict-createdictEmpty dict
dict-getdict, keyvalueGet value (fails if absent)
dict-setdict, key, valdictSet key (returns new dict)
dict-removedict, keydictRemove key
dict-has?dict, keybooleanCheck if key exists
dict-keysdictlistAll keys
dict-valuesdictlistAll values
dict-sizedictintegerNumber of entries
dict-merged1, d2dictMerge (d2 wins on conflict)
dict-set!dict, key, valdictSet key in-place (mutating)

Console / Debug

PrimitiveInputsOutputDescription
logvalue(s)Print to console
inspectvaluevaluePrint and pass through
breakpointPause if debugger attached

Type Checking

PrimitiveInputsOutputDescription
type-ofvaluestringType name
integer?valuebooleanIs integer?
real?valuebooleanIs real?
string?valuebooleanIs string?
list?valuebooleanIs list?
null?valuebooleanIs null?
error?valuebooleanIs error?
class-ofobjectstringClass name of an object
instance-of?obj, class_namebooleanIs instance of named class?
responds-to?obj, method_namebooleanDoes object have named method?
conforms-to?obj, protocol_namebooleanDoes object conform to named protocol?

Enum

PrimitiveInputsOutputDescription
enum-createtype, variant, data_listenumCreate an enum value with type name, variant name, and associated data
enum-typeenumstringGet the enum type name
enum-variantenumstringGet the variant name
enum-dataenumlistExtract associated data as a list

Method References

Method references are first-class values that point to a method. They can be passed to higher-order primitives like map, filter, and reduce, or called dynamically with call.

PrimitiveInputsOutputDescription
method-refname_stringrefCreate a reference to a universal method by name
method-ref-classclass, methodrefReference to a specific class method
method-ref-boundobject, methodrefReference bound to a specific object instance
callref, argresultInvoke a method-ref with an argument
is-method-ref?valuebooleanTest if value is a method reference
method-ref-namerefstringGet the method name from a reference

Networking

PrimitiveInputsOutputDescription
http-geturlbody_stringHTTP GET request, returns response body as string
http-posturl, body, content_typeresponse_stringHTTP POST request with body and content type

JSON

PrimitiveInputsOutputDescription
json-parsestringvalueParse JSON string into a Phograph value (dict, list, etc.)
json-encodevaluestringEncode a Phograph value as a JSON string

Date/Time

PrimitiveInputsOutputDescription
date-nowdateCurrent date/time
date-createY, M, D, h, m, sdateCreate from year, month, day, hour, minute, second
date-componentsdatelistExtract [year, month, day, hour, minute, second]
date-adddate, secondsdateAdd seconds to a date
date-diffdate1, date2secondsDifference between two dates in seconds
date-formatdate, format_stringstringFormat date as string (e.g., "yyyy-MM-dd")
date-parsestring, format_stringdateParse string into date using format
date-weekdaydateintegerDay of week (0=Sunday through 6=Saturday)
date-comparedate1, date2integerCompare dates: <0 if before, 0 if equal, >0 if after
date-from-timestampnumberdateCreate date from Unix timestamp (seconds since epoch)
date-to-timestampdatenumberConvert date to Unix timestamp

Duration Helpers

Convenience primitives for creating durations in seconds, suitable for use with date-add:

PrimitiveInputsOutputDescription
secondsnnIdentity (n seconds)
minutesnn * 60Convert minutes to seconds
hoursnn * 3600Convert hours to seconds
daysnn * 86400Convert days to seconds

Observables

Observables let you watch for changes to object attributes and react automatically. They are the foundation for reactive data binding in front panels and between objects.

PrimitiveInputsOutputDescription
observeobject, attr_nameobserver_idWatch a specific attribute for changes
unobserveobject, observer_idnullStop watching a specific observer
observe-anyobjectobserver_idWatch all attributes on an object for any change
bindsrc_obj, src_attr, tgt_obj, tgt_attrobserver_idBind source attribute to target — changes propagate automatically
unbindobject, observer_idnullRemove a binding

Control Flow

Success and Failure

Every node execution has one of three outcomes:

  1. Success — completed normally, produced output.
  2. Failure — a controlled outcome that triggers a control annotation. Not an error.
  3. Error — an unrecoverable problem.

Control Annotations

An annotation on a node tells the engine what to do on success or failure. The most important annotations:

AnnotationMeaning
nextCaseOnFailureIf this node fails, try the next case
nextCaseOnSuccessIf this node succeeds, try the next case
continueOnFailureIf this node fails, skip it and continue
terminateOnSuccessIf this node succeeds, exit the loop
terminateOnFailureIf this node fails, exit the loop
failOnFailurePropagate failure to the caller

The if Primitive

For simple conditionals within a single case: if(condition, then-value, else-value) outputs whichever value matches the condition.

Loops and List Annotations

Loop (counted)

Created by wrapping a method call. The output of each iteration feeds back as input for the next. A terminate annotation ends the loop.

Repeat (indefinite)

Executes repeatedly with no counter until a terminate or finish annotation fires inside.

List Annotation (ellipsis)

Mark an input with an ellipsis: the operation runs once per list element. Mark the output too: results are gathered into a list. This turns any scalar operation into a map over a list — no explicit loop needed.

Spreads (automatic broadcasting)

A scalar operation that receives a List on an input automatically executes once per element, producing a List of results. Multiple list inputs are zipped. This makes most explicit loops unnecessary.

Classes and Objects

Defining a class

A class has attributes (data) and methods (behavior). Attributes can be instance-level or class-level. Methods receive the object as their first input (self).

Instance generators

An octagonal node creates a new object. If the class has an init method, it runs automatically. Extra inputs on the generator are passed to init.

Get and Set

Get reads an attribute: 1 input (object), 2 outputs (object pass-through, value). Set writes an attribute: 2 inputs (object, value), 1 output (modified object).

Inheritance

Single inheritance. A child class inherits all attributes and methods from its parent. Methods can be overridden. Use /methodName for dynamic dispatch based on the object’s class.

Method call syntaxes

SyntaxDescription
/MethodNameDynamic dispatch — determined by the first input’s class
//MethodNameContext dispatch — within the same class
ClassName/MethodNameExplicit — call a specific class’s method

Protocols

A protocol defines a set of method signatures that a class must implement to conform. Protocols enable polymorphism without inheritance — any class can conform to any protocol by providing the required methods.

Use conforms-to? to test at runtime whether an object’s class conforms to a given protocol. Protocol conformance is declared on the class definition and checked by the runtime.

Actor Classes

An actor class is declared with the actor keyword in its class definition. Actors provide concurrency safety: all method calls on an actor are serialized, ensuring that only one method executes at a time on a given actor instance. This eliminates data races without manual locking.

Actor methods are implicitly asynchronous — calls from outside the actor return a Future that resolves when the method completes. Calls within the same actor execute synchronously.

Front Panel

A front panel is a SwiftUI interface automatically generated from a class’s attributes. Each attribute with a front-panel annotation becomes an interactive control (slider, text field, toggle, color picker, etc.) bound to the attribute’s value via the observable system.

When the user changes a control, the bound attribute updates. When code changes the attribute, the control updates. This two-way binding is established automatically using bind.

Error Handling

An Error is a value (not an exception) with a message, code, and optional details dict.

PrimitiveDescription
error-createCreate an error value
error-messageGet message string
error-codeGet code string
error?Test if a value is an error
fail-withFail the current method with an error

The try annotation catches failures and converts them to Error values on a dedicated error output pin (visually a dashed red wire).

Error cluster wiring: fallible operations can chain an error-in/error-out pin. If any operation fails, subsequent operations skip and the error flows through to the end.

Concurrency and Async

Async operations return Futures. A Future resolves later; downstream operations automatically wait for the value (no explicit await needed — it falls out of the firing rule).

PrimitiveDescription
future-valueBlock until resolved
future-resolved?Non-blocking check
future-thenChain a callback
future-allWait for all futures
future-anyWait for first future
dispatchRun method on background thread

Channels enable producer-consumer patterns: channel-create, channel-send, channel-receive.