Definition of the FROG value type system for .frog programs
FROG — Free Open Graphical Language
- 1. Overview
- 2. Scope
- 3. Goals of the Type System
- 4. Scope for v0.1
- 5. Relation with Other Specifications
- 6. Location in a
.frogFile - 7. Canonical Type Expressions
- 8. Built-in Types
- 9. Type Identity
- 10. Type Compatibility Model
- 11. Implicit Numeric Coercions
- 12. Implicit Array Coercions
- 13. Typed Value Compatibility Contexts
- 14. Explicit Conversion Nodes
- 15. Validation Rules
- 16. Out of Scope for v0.1
- 17. Examples
- 18. Summary
The FROG type system defines how value types are described, serialized, validated, connected, and converted inside a
.frog program.
For v0.1, the type system is intentionally small, explicit, and durable. It provides:
- a fixed set of built-in primitive scalar types,
- a minimal built-in array type system,
- a canonical textual type-expression syntax for source serialization,
- deterministic type identity, compatibility, and coercion rules.
The type system applies to value-carrying elements such as:
- public interface ports,
- diagram ports and edges,
- structure boundaries,
- widget primary values,
- typed widget properties or method values when those members are typed,
- typed configuration values such as
defaultorinitial.
This document defines value types only. It does not define widget classes, widget object references, public interface ownership, control-structure semantics, scheduling, execution order, or runtime memory policy.
This document also does not define repository-wide version policy.
Top-level spec_version identifies the source-format compatibility target of the containing .frog file, while the published specification corpus version remains governed centrally in Versioning/Readme.md.
This document specifies:
- canonical source-level type expressions,
- built-in primitive and array types,
- type identity rules,
- type compatibility rules,
- language-defined implicit coercion rules for values.
This document does not specify:
- widget object identity or widget-reference semantics,
- execution scheduling,
- cycle legality,
- primitive-local behavior except where typed compatibility is referenced,
- future runtime, lowering, or IR type systems beyond the canonical source type layer,
- the repository-wide version-transition doctrine.
The FROG type system is designed to satisfy the following goals:
- Clarity — types MUST be readable and unambiguous in source form.
- Determinism — type identity, compatibility, and coercion behavior MUST be explicitly defined.
- Portability — type meaning MUST NOT depend on platform-specific compiler behavior.
- Graph usability — the system SHOULD support practical graphical programming with limited but useful implicit coercions.
- Durability — canonical type expressions SHOULD remain stable across editors, validators, runtimes, and future tooling layers.
- Separation of concerns — value typing MUST remain distinct from widget object models, public interface ownership, authoring metadata, and execution scheduling.
FROG v0.1 standardizes a minimal built-in value type system consisting of:
- primitive scalar types,
- array types,
- canonical type-expression syntax,
- type identity rules,
- type compatibility rules,
- implicit numeric coercion rules,
- implicit array coercion rules based on element coercion and shape preservation.
FROG v0.1 does not yet standardize:
- user-defined named types,
- library-defined custom types,
- records or structs,
- enums,
- class types,
- sum or variant types,
- generic type declarations beyond arrays,
- units-of-measure type systems.
Under the centralized cumulative version model, later source-format versions should normally extend earlier valid type forms rather than silently replace them, unless an explicit breaking boundary is declared in repository-wide version governance.
This document defines the value type system used by other parts of the FROG specification.
Interface.mduses the type system for public input and output ports.Diagram.mduses the type system for graph ports, edges, structure boundaries, and typed configuration fields.Widget.mduses the type system for thevalue_typeof value-carrying widgets.Front panel.mdserializes widgets that may declare avalue_type, but does not redefine the type system.Widget interaction.mdmay reference typed widget members, but does not redefine type identity or coercion behavior.Libraries/Core.mdrelies on this document for primitive port typing and typed configuration compatibility such asfrog.core.delay.initial.Versioning/Readme.mddefines the centralized distinction between specification corpus version, top-levelspec_version, and program artifact versioning.
Ownership boundary:
Type.md owns:
- value type expressions
- value type identity
- value compatibility
- implicit value coercion rules
Type.md does not own:
- widget object semantics
- interface ownership structure
- executable graph semantics
- cycle legality
- primitive-local behavioral semantics
- repository-wide version-transition law
Types are represented directly where values are declared or constrained. For example, interface ports and other typed elements use canonical textual type expressions such as:
{
"id": "input_signal",
"type": "f64"
}
The top-level .frog file does not require a separate mandatory types section in v0.1.
Type declarations are embedded inline through canonical type expressions.
This keeps type usage local to the source object that owns the typed value.
Accordingly:
- top-level
spec_versionidentifies the source-format compatibility target of the file, - inline
typefields identify value-type meaning inside that source format, - the published specification corpus version remains governed centrally in
Versioning/Readme.md.
FROG v0.1 uses a canonical textual syntax to represent types in source files. This syntax is normative.
Primitive types are written as a single identifier:
bool
i8
i16
i32
i64
u8
u16
u32
u64
f32
f64
stringDynamic-size arrays are written as:
array<T>Fixed-size arrays are written as:
array<T, N>Where:
Tis any valid FROG v0.1 type expression,Nis a positive integer literal.
- Type names are case-sensitive.
- No alternative aliases are defined in v0.1.
float64,int32,double, and similar aliases are not canonical type names.- Whitespace inside a type expression is not significant for parsing, but canonical serialized form SHOULD omit unnecessary spaces.
Examples of canonical form:
i32
f64
array<u8>
array<f32, 256>
array<array<i16, 8>, 4>bool— logical true/false value.
i8— 8-bit signed integeri16— 16-bit signed integeri32— 32-bit signed integeri64— 64-bit signed integer
u8— 8-bit unsigned integeru16— 16-bit unsigned integeru32— 32-bit unsigned integeru64— 64-bit unsigned integer
f32— 32-bit IEEE 754 floating-point valuef64— 64-bit IEEE 754 floating-point value
string— textual value.
The exact internal encoding and memory representation of string are not further standardized in v0.1.
Only its identity as a distinct built-in type is defined here.
FROG v0.1 defines two built-in array forms:
array<T>— array of element typeTwith dynamic size,array<T, N>— array of element typeTwith fixed sizeN.
Array rules:
- arrays are homogeneous,
- nested arrays are allowed,
- fixed-size length is part of type identity,
- dynamic-size and fixed-size arrays are distinct types.
Examples:
array<f64>
array<u8, 1024>
array<array<f32, 16>, 8>Two types are identical if and only if their canonical meaning is exactly the same.
i32is identical toi32.i32is not identical tou32.f32is not identical tof64.stringis not identical toarray<u8>.
array<f64>is identical toarray<f64>.array<f64, 16>is identical toarray<f64, 16>.array<f64, 16>is not identical toarray<f64, 32>.array<f64>is not identical toarray<f64, 16>.array<i32>is not identical toarray<u32>.
Type identity is based on canonical meaning, not on author formatting choices. Whitespace differences that do not change parsing do not change identity.
Type compatibility in FROG v0.1 is defined by the following categories:
- Exact match — source and target types are identical.
- Implicit coercion allowed — source and target types are different, but FROG defines deterministic automatic conversion.
- Explicit conversion required — no implicit coercion is allowed, but an explicit conversion node may make the connection legal.
- Incompatible — the types are not compatible under v0.1 rules.
Unless otherwise defined, a typed connection or assignment context is valid when:
- the source and target types are identical, or
- an implicit coercion is defined by this specification, or
- an explicit conversion node is inserted by the author.
Compatibility decision model
source type
|
v
target type
|
+-- identical? -------------------- yes --> valid
|
+-- implicit coercion defined? ---- yes --> valid
|
+-- explicit conversion inserted? - yes --> valid
|
+-- otherwise --------------------------- invalidThis document defines value-type compatibility only. It does not define widget-reference compatibility, UI sequencing compatibility, or execution ordering semantics.
FROG v0.1 allows implicit coercions between numeric scalar types. These coercions are part of the language-defined value-compatibility rules. They are not user-authored diagram nodes in source form.
An IDE MAY visually indicate an implicit coercion, for example by displaying a coercion marker on the target terminal. Such visualization is an IDE concern and does not require an explicit source node.
For the purpose of implicit coercion, the numeric built-in types are:
i8,i16,i32,i64u8,u16,u32,u64f32,f64
The following built-in types are not numeric:
boolstring
Any numeric scalar type MAY be implicitly coerced to any other numeric scalar type. The conversion result MUST follow the rules below.
When converting from one integer type to another:
- if the source value is representable in the target type, the result is the exact converted value,
- if the source value is below the minimum representable target value, the result is the minimum target value,
- if the source value is above the maximum representable target value, the result is the maximum target value.
This is a saturating conversion.
When converting from an integer type to a floating-point type:
- the conversion follows IEEE 754 numeric semantics for the target floating-point representation,
- if the integer magnitude exceeds the representable range of the target floating-point type, the result is
+Infor-Infas appropriate.
When converting from one floating-point type to another:
- the conversion follows IEEE 754 semantics for the target type,
- finite values are converted to the nearest representable value according to the target representation,
NaNremainsNaN,+Infremains+Inf,-Infremains-Inf.
When converting from a floating-point type to an integer type:
- finite values are first truncated toward zero,
- the truncated value is then saturated to the representable range of the target integer type,
NaNconverts to0,+Infconverts to the maximum representable target value,-Infconverts to the minimum representable target value.
No implicit coercion is defined in v0.1 for:
bool↔ numericstring↔ numericbool↔string
FROG v0.1 allows implicit coercion between array types when:
- coercion is valid for the array element type, and
- array shape remains compatible.
If T may be implicitly coerced to U, then:
array<T>MAY be implicitly coerced toarray<U>,array<T, N>MAY be implicitly coerced toarray<U, N>.
The coercion is applied element by element.
Implicit array coercion never changes array shape.
array<i32, 16>toarray<f64, 16>is allowed.array<i32, 16>toarray<f64, 32>is not allowed.array<i32>toarray<f64>is allowed.array<i32>toi32is not allowed.
Dynamic and fixed-size arrays are distinct types. No implicit coercion is defined in base v0.1 between:
array<T>andarray<T, N>,array<T, N>andarray<T>.
A stricter profile or future extension MAY define explicit conversion mechanisms for such cases, but they are not implicit in v0.1.
Nested arrays follow the same rule recursively. If the inner element-type coercion is valid and shape is preserved at each fixed-size level, the nested array coercion is valid.
The following source contexts rely on type compatibility in v0.1:
A diagram edge connecting a source output port to a destination input port is type-valid only if:
- the source and destination value types are identical, or
- an implicit coercion is defined by this document, or
- an explicit conversion node is inserted.
An interface input default value, when present, MUST be compatible with the declared port type.
This document defines that compatibility.
A value-carrying widget value_type MUST be a valid canonical FROG type expression.
When the widget participates naturally in the diagram through widget_value,
the carried value MUST follow that declared type.
When widget properties or method parameters or results are typed, those types MUST also use valid FROG type expressions or profile-defined extensions compatible with this document.
For typed stateful primitives such as frog.core.delay:
- the carried input and output value type MUST be well-defined,
- the
initialvalue MUST be compatible with that carried type.
This document defines compatibility. The state semantics themselves are defined elsewhere.
When a structure boundary declares a value type, all boundary crossings MUST satisfy the same compatibility rules defined here.
FROG implementations MAY define explicit conversion nodes such as numeric conversion, cast, or type-adaptation nodes. These nodes are distinct from implicit coercions.
An explicit conversion node:
- is authored intentionally in the diagram,
- is represented as a real node in source form,
- MAY be used when no implicit coercion is defined,
- MAY also be used to make author intent explicit even when an implicit coercion would be legal.
The exact standardized library of explicit conversion nodes is outside the scope of this document in base v0.1. However, the distinction between:
- implicit language-defined coercion, and
- explicit authored conversion
is normative.
A FROG validator MUST apply the following rules:
- every type expression MUST parse successfully according to this specification,
- every referenced built-in type name MUST be valid,
- every fixed array size
NMUST be a positive integer literal, - connections between typed ports MUST be accepted only if they are exact matches, implicitly coercible, or explicitly converted through a node,
- implicit numeric coercions MUST follow the deterministic rules defined in this document,
- implicit array coercions MUST preserve shape,
- implicit coercion between dynamic and fixed-size arrays MUST be rejected in base v0.1,
- typed configuration values such as
defaultorinitialMUST be compatible with their declared type.
A validator MAY report warnings or informational diagnostics for implicit coercions, but if a coercion is defined by this specification, the typed connection is valid.
Validators SHOULD diagnose at least the following error classes:
- invalid canonical type expression,
- unknown built-in type name,
- invalid fixed array size,
- shape-changing implicit array coercion,
- attempted implicit dynamic/fixed array coercion,
- disallowed implicit coercion between non-numeric scalar types,
- type-incompatible
defaultvalue, - type-incompatible
initialvalue.
These checks validate canonical value-type form and value-compatibility behavior.
They do not, by themselves, redefine top-level spec_version policy or repository-wide corpus-version governance.
- user-defined named types,
- records or structs,
- enums,
- class types,
- sum or variant types,
- generic type declarations beyond arrays,
- standardized custom type registries,
- ownership and borrowing systems,
- units-of-measure type systems,
- implicit coercion between dynamic and fixed-size arrays.
bool
i32
f64
stringarray<f64>
array<u8, 1024>
array<array<i16, 8>, 4>i32andi32are identical.i32andu32are not identical.array<f64, 16>andarray<f64, 16>are identical.array<f64, 16>andarray<f64, 32>are not identical.array<f64>andarray<f64, 16>are not identical.
i32→f64u8→i16f64→i32array<i32, 16>→array<f64, 16>array<i32>→array<f64>
bool→i32string→f64array<i32, 16>→array<f64, 32>array<i32>→i32array<f64>→array<f64, 16>array<u8, 16>→array<u8>
"interface": {
"inputs": [
{ "id": "signal", "type": "array<f64>" },
{ "id": "gain", "type": "f64" },
{ "id": "enable", "type": "bool", "connection": "optional", "default": true }
],
"outputs": [
{ "id": "scaled", "type": "array<f64>" }
]
}{
"id": "ctrl_gain",
"role": "control",
"widget": "frog.ui.standard.numeric_control",
"value_type": "f64"
}{
"id": "delay_1",
"kind": "primitive",
"type": "frog.core.delay",
"initial": 0.0
}
In this example, the initial value is compatible with a floating-point carried value type.
The FROG type system provides the canonical value-type foundation for v0.1.
- It defines built-in primitive and array types.
- It defines canonical textual type expressions.
- It defines exact type identity and deterministic compatibility rules.
- It allows limited implicit coercions for numeric and shape-preserving array cases.
- It rejects implicit coercion for non-numeric scalar mixes and for dynamic/fixed array mismatches.
- It remains separate from widget identity, widget references, and UI object semantics.
- It does not define source-format compatibility law or published specification corpus versioning.
This gives FROG a simple, explicit, portable, and durable type foundation suitable for graphical programming, validation, and future execution-oriented lowering.