Bliss Language Specification
Data, Actions, Binded Types, and Calling Conventions
Status: Draft Specification Audience: Language users, compiler implementers Scope: Core type system, semantics, and call boundaries
1. Design Principles
Bliss is built around one central idea:
Meaning is chosen, never inferred.
From this follow several non-negotiable principles:
- No implicit behavior
- No implicit meaning
- No semantic widening
- No global coherence rules
- All semantic changes are explicit and local
The type system exists to enforce these principles mechanically.
2. Type System Overview
Bliss has three distinct kinds of types, all first-class and mutually disjoint:
- Raw Data Types – memory only
- Action Types – behavior only
- Binded (Semantic) Types – memory + behavior + meaning
None of these is derived implicitly from another. Transitions between them must be explicit.
3. Raw Data Types
3.1 Definition
A data type defines memory layout only.
data Data {
hide u64 byte_ptr;
private u32 size;
}Properties:
- Define storage and representation
- Contain no behavior
- Carry no meaning
- Expose no methods
- Possess open semantics
A value of a data type is called raw data:
Data a;3.2 Raw Data Semantics
Raw data values expose no members:
a.length(); // errorHowever, raw data may be reinterpreted explicitly:
a::X.length(); // valid
a::Y.length(); // validRule: Raw data has no inherent behavior, but may be interpreted under any action or bind.
4. Action Types
4.1 Definition
An action represents a behavioral capability, independent of storage and meaning.
action X {
fx length(self) -> int;
}Properties:
- Have no storage
- Have no instances
- Are not data
- Do not define meaning
- May be used as parameter types
Actions describe required behavior, not semantic intent.
4.2 Action Annotations
Actions may be annotated to refine validity rules:
@reinterpret
action UTF8 {
fx length(self) -> int;
}
@composable
action Printable {
fx print(self);
}Annotations:
- Affect validity and usage rules
- Never affect memory representation
5. Binded (Semantic) Types
5.1 Definition
A bind is how we interpret an action, or a set of actions towards some data. This binding produces a interpretation token that can be used to add meaning to a data.
bind X, Data as SomeType {
fx length(self) -> int { ... }
}Facts:
- It is neither
DatanorX - It participates fully in the type system
- It has closed semantics
5.2 Construction of Binded Values
A binded value is constructed explicitly:
Data::SomeBinding b;
Data::(BindA, BindB) c; //multi bindingsThis is semantic construction, not reinterpretation.
6. Open vs Closed Semantics
6.1 Open Semantics (Raw Data)
Data a;Characteristics:
- No meaning
- No guarantees
- Reinterpretation allowed
a::X.length();
a::Y.length();6.2 Closed Semantics (Binded Types)
Data::SomeBind b;Characteristics:
- Meaning is fixed
- Semantics are sealed
- Reinterpretation is forbidden
b.length(); // valid
b::X.length(); // errorRule: Only raw data may be reinterpreted.
7. Value-Level Action Composition
A value may expose multiple actions simultaneously:
Type::(X, Y) k;This means:
- Underlying data is unchanged
- Both actions are available
- No new semantic type is created
- Composition is local to the value
Rules:
- Composition does not create a bind
- No additional actions may be introduced later
- Ambiguity must be resolved explicitly
k.length::X();
k.length::Y();8. Binding
A bind associates a non-empty set of actions with a data type by creating an interpretation token.
An interpretation token does not introduce a new nominal data type. Instead, it establishes a sealed semantic relationship between:
- A specific data type (memory), and
- A specific unordered set of actions (behavior).
bind (X, Y, Z), Data as A;Here, A is an interpretation token representing:
InterpretationToken = (Data, ActionSet)Actions themselves are not interpretation tokens.
Xis an action.Datais memory.Ais the interpretation token joining them.
Therefore:
Data::A // valid
Data::X // illegal
Data::(X,Y) // illegalOnly interpretation tokens may be used with the :: operator.
8.1 Binding Rules
- The action set must be non-empty.
- The action set is unordered.
- Duplicate actions are illegal.
- The action set is sealed at bind time.
- No additional actions may be introduced after binding.
- An interpretation token is uniquely identified by its data type and exact action set.
- Binding does not imply subtyping relationships.
8.2 Identity of Interpretation Tokens
Interpretation tokens are nominal declarations.
Each bind statement introduces a distinct interpretation token, even if:
- The underlying data type is identical, and
- The action sets are identical.
Example:
bind (X), Data as A;
bind (X), Data as B;A and B are distinct interpretation tokens.
They represent separate semantic commitments, even though they relate the same data type and action set.
Interpretation token identity is therefore defined by declaration identity, not by structural equivalence of (Data, ActionSet).
No structural widening relationship exists between tokens.
8.3 Closed Semantics
An interpretation token defines a closed semantic universe.
For a value:
Data::A v;- Only actions in the token’s action set are accessible.
- Reinterpretation into additional actions is forbidden.
- Semantic widening is illegal.
8.4 Prohibited Forms
The following are illegal:
bind (), Data as A; // empty action set
bind (X, X), Data as A; // duplicate actions
Data::X; // action is not a token
Data::(X,Y); // actions are not tokensBinding never implies structural subtyping, automatic widening, or action inference.
9. Method Resolution
9.1 Unqualified Calls
value.method();Legal only if:
- The value is binded
- Exactly one bound action defines
method
9.2 Qualified Calls
value.method::X();Legal only if X is part of the value’s bind or value-level composition.
Qualification never introduces new semantics.
10. Function Parameters and Calling Conventions
Function parameters explicitly determine which semantic layer is preserved across the call boundary.
Bliss supports four distinct parameter forms:
- Data parameters (representation only)
- Binded parameters (semantic identity preservation)
- Action parameters (behavior-only polymorphism)
withparameters (representation + capability constraint)
10.1 Data Parameters (Semantic Erasure)
fx someStuff(Data x);Rules:
- Accepts any binded type with underlying
Data - All actions and meaning are stripped at the boundary
- Only raw data is visible inside the function
- Implicit stripping may produce a compiler warning (equivalent to
ext)
Data::A a;
Data::B b;
Data::(A, B) c;
someStuff(a); // valid (semantic erasure)
someStuff(b); // valid
someStuff(c); // valid10.2 Binded Parameters (Semantic Preservation)
fx process(Data::A a);Rules:
- The provided bind must contain at least the action set required by
A - Compatibility is defined by action-set inclusion
- Meaning associated with
Ais guaranteed inside the function - No reinterpretation is allowed
If S(T) denotes the action set of bind T, then:
Data::T satisfies Data::A
iff
S(A) ⊆ S(T)process(a); // valid
process(b); // invalid
process(c); // valid if c contains at least A's action set10.3 Action Parameters (Behavioral Polymorphism)
fx doThing(x with ActionType);Rules:
- Any binded type implementing
ActionTypeis accepted - Underlying data is inaccessible
- Only behavior defined in the action is visible
- No semantic identity is preserved
Data1::X a;
Data2::Y b;
//X binds ActionType with Data, Y does not
doThing(a); //valid
doThing(b); //invalid10.4 with Parameters (Representation + Capability)
fx log(u8[][] str with (ActionGroup));The with form defines a compound parameter type consisting of:
- An exact underlying data type
- A required action set
Rules:
- The underlying data type must match exactly
- The provided bind must contain the required action set
- Both representation and capability are available inside the function
- No specific interpretation token is required
Formally, a value Data::T satisfies:
D with (A1, A2, ..., An)iff:
- The underlying data type is exactly
D {A1, A2, ..., An} ⊆ S(T)
This differs from :::
Data::Arequires a specific semantic interpretationData with (A)requires capability but not token identity
The with construct does not create new semantic identity. It expresses a boundary constraint combining representation and behavior.
11. Explicit Semantic Projection
11.1 Data Extraction (ext)
ext explicitly destroys meaning and behavior, recovering raw data.
Data x = ext Data::(A, B, C, D);Rules:
- All actions are removed
- Result has open semantics
- No meaning is preserved
11.2 Reinterpretation After Extraction
Data::B y = (ext Data::A)::B;Rules:
- Extraction always precedes reinterpretation
- This is legal only because the intermediate value is raw data
- Semantic changes are explicit and local
12. Invalid Semantic Operations
The following are always illegal:
b::Y.length(); // semantic widening
b.length::Y(); // accessing non-existent semanticsBinded types define a closed semantic universe.
13. Reinterpretable Actions and Guidelines
Actions annotated with @reinterpret assign meaning to raw data.
@reinterpret
action UTF8 { }Guidelines:
- Prefer using one reinterpretation at a time
- Multiple reinterpretations may conflict
- The compiler does not enforce semantic consistency
Binded types exist to lock meaning and avoid ambiguity.
14. Type aliasing and qualified names
Type definitions where binders are used a lot can be a handful to write. To tackle this, a syntactic sugar for type aliasing is suggested.
using std.actions.summary;
data UserData {
String name;
int age;
}
bind summary, UserData as UserBinder {
fx summarize(self) {
return "Name: { self.name }\nAge: { self.age }"
}
}
alias UserData::UserBinder as User; //this is just a dumb rename
alias UserData::UserBinder as SameButDifferent; //again dumb rename15. Standalone Bindings
Whenever we bind some data, we might want a standalone type without type aliasing.
By default, all bindings are types as well, so they can stand solo as well.
bind summary, UserData as User { ... } //implementationsso the User is a bound semantic type formed from UserData and the summary action.
16. Singleton Actions and Bindings
In some situations, a behavior consists of a single action function that does not naturally belong to a larger conceptual group. Creating a dedicated action block in such cases adds syntactic structure without providing additional semantic value.
To address this, Bliss provides a singleton action syntax as a purely syntactic convenience. This allows single-responsibility behaviors to be expressed directly, without forcing artificial grouping.
16.1 Singleton Action Definition
A singleton action can be declared inline using the following form:
action fx Walkable.walk(self) -> void;This declaration is syntactic sugar for an action containing exactly one function. It does not introduce a new kind of action, nor does it weaken the action model in any way. All semantic rules applicable to regular actions continue to apply unchanged.
16.2 Singleton Binding
A singleton action may be bound directly to a data type using an inline bind expression:
bind Walkable.walk(self) {
self.steps += 20;
}, DogData as WalkableDog;This form is semantically equivalent to a standard bind involving a single-action action type. The resulting type WalkableDog is a nominal binded (semantic) type with closed semantics, exactly as defined by the core binding rules.
16.3 Aliasing as a Unified View
If a simpler or more expressive name is desired, the resulting binded type may be aliased:
alias DogData::WalkableDog as Dog;This alias introduces no new meaning. It merely provides a more convenient name for an existing semantic type.
16.4 Rationale
Singleton actions exist to support single-responsibility behaviors without encouraging premature or artificial abstraction. They reduce boilerplate, improve readability, and make refactoring easier, while remaining fully consistent with the Data–Action–Bind model.
No runtime overhead is introduced, and all semantics remain explicit and local.
17. Field Values
In Bliss, fields inside a data definition may take different forms depending on how they are intended to be accessed and mutated. By default, all fields are readable and writable from any context.
Bliss refines this behavior using a small set of field modifiers. These modifiers do not change the DAOP model itself; instead, they precisely control visibility and mutability of fields.
The available modifiers are:
hideprivatereadonly
They may be used individually or in combination.
17.1 hide
hide is the strongest field modifier.
A hide field is:
- Visible only during construction of the data value
- Completely inaccessible afterward
- Not readable or writable by actions
- Not part of the observable semantic state
hide fields exist solely for runtime or native-layer mechanisms, such as internal handles, storage anchors, or capability tokens.
They are intentionally excluded from DAOP reasoning.
17.2 private
private restricts field visibility to actions only.
A private field is:
- Invisible to user-level code
- Fully accessible within actions bound to the data
- Part of the semantic state of the data
This modifier is intended for internal state that affects behavior but should not be exposed as part of the public data surface.
17.3 readonly
readonly restricts when a field may be written.
A readonly field:
- May be assigned only during construction
- Cannot be overwritten outside of actions
- Remains readable according to its visibility rules
This modifier enforces immutability guarantees while preserving DAOP semantics.
17.4 Combined Modifiers
Modifiers may be combined to further refine behavior.
readonly hide / hide readonly
This combination produces a field that:
- Is visible only to actions
- Can be assigned only during construction
- Cannot be mutated afterward
Such fields are useful for internal constants, cached metadata, or invariant state that should remain stable throughout the lifetime of the data value.
Summary
Field modifiers in Bliss refine access and mutability without altering the DAOP model itself:
hideremoves a field from semantic visibility entirelyprivatelimits visibility to actionsreadonlylimits mutability to construction time
Together, these modifiers allow precise control over data representation while keeping data–action separation explicit and intact.
18. Canonical Summary
Data defines memory.Actions define behavior.Binded types define meaning.
Meaning is chosen, never inferred.
End of specification.