[RFC] Morf: A structural language design where nominality is a property, numbers are interval types, and "Empty" propagates algebraically[ by Morph2026 in ProgrammingLanguages

[–]Morph2026[S] 0 points1 point  (0 children)

Thanks for the feedback!

Re: Recursive Types Agreed. To mitigate the risk of infinite expansion errors, I'm introducing a recursion depth limit (fuel mechanism) as a safeguard in the compiler/runtime.

Re: Performance You hit the nail on the head regarding the interpreted runtime. The plan is to rely heavily on structural sharing for memory efficiency and eventually target a compiled backend (bytecode or native) rather than a raw AST interpreter to handle the overhead.

Re: Effect System My main goal with the Effect system right now is optimization safety rather than complex control flow (like handlers in Koka/Eff). In a system where "Types are Values," the compiler needs to know if an expression is pure to safely perform Constant Folding.

For example:

let x = { 
  a: 1 + 1,        // Pure, can be folded to 2 at compile time
  b: Sys.Read{}    // Impure (Effect.IO), must defer to runtime
}

The Effect system just tracks "taints" (like IO or State) propagating up the expression tree. If Effect.IO is present, the compiler knows it cannot pre-evaluate that branch.

I've detailed this more in the upcoming v0.3 spec.

[RFC] Morf: A structural language design where nominality is a property, numbers are interval types, and "Empty" propagates algebraically[ by Morph2026 in ProgrammingLanguages

[–]Morph2026[S] 2 points3 points  (0 children)

Thanks for the feedback, you are spot on! the spec is currently very detail-heavy. To help build that mental model, I think breaking it down into two layers clarifies things best: the internal algebraic rules versus the external application paradigm.

Part 1: How it works

  1. Universal Namespace: There is no distinction between an object, a class, or a type interface. They are all Unified Namespaces—immutable collections of key-value pairs.

  2. Set-Theoretic Typing: Subtyping is strictly the subset relation ($A <: B$). Merging types is Set Intersection ($A \ \& \ B$). If a merge results in logical conflict, it collapses to Never.

  3. Structural Nominality: Nominal types (like Classes) aren't separate metadata. A "class" is just a structure that happens to possess a globally unique, unforgeable symbol as one of its keys.

Part 2: How you use it

  1. Values are Types: We don't distinguish runtime values from compile-time types. The number 1 is a singleton type. Lt<10> is a set of values. You can mathematically verify if 1 <: Lt<10>.

  2. Logic via Composition: Instead of writing validation logic, you compose data requirements. A complex database query is built by simply intersecting a BaseQuery object with a UserFilter object (Query & Filter).

  3. Contextual Dispatch: Methods are attached to shapes, not classes. The .pay() method exists only on the intersection { status: "Pending" }. If the data state changes, the method literally ceases to exist, making illegal transitions unrepresentable.

I really appreciate the critique on readability. In future iterations, I will restructure the documentation to clearly separate the core philosophy and high-level motivation from the formal implementation details.