Skip to content

How FOAM is Different

Kevin Glen Roy Greer edited this page Jan 16, 2026 · 3 revisions

Question: Many people have tried to build a practical Model-based programming environment but these efforts always seem to collapse under their own weight. What are you doing differently this time? (Source: http://alarmingdevelopment.org/?p=926)

Short Answer

  • We never edit or check-in generated code.
  • Design Patterns make modelling and meta-programming viable.
  • We generate fine-grained components and compose them with Contexts and Facades.
  • We augment rather than replace the target language.
  • We rely on a small set of strong canonical interfaces.
  • We're Feature-Oriented.
  • Active-Models are retained at runtime allowing for data-driven programming.
  • FOAM is itself modelled, making it small and uniform.
  • Models are first-class data.
  • Models can be edited/viewed in our MVC framework.
  • Functional Reactive Programming applied to Models enables live-coding.

Long Answer

Code is a Liability

Modelling-tools and code-generators produce large quantities of code, but this code invariably needs editing to add additional or custom behavior. After generating code, developers would load it in their editor and search for /* insert code here */ comments. While these tools provided a good starting point, developers quickly accumulated more code than they could maintain. It's like winning a luxury home in a lottery, but then not being able to afford the taxes.

The fundamental problem: code requires maintenance and is therefore a liability, not an asset. Code-generators are actually liability generators. The real asset is the features that code supplies. What you want is the features, not the code itself.

Our solution is simple: never generate any code that needs to be edited or checked in. Generated code should exist only as part of the build process or runtime, never in version control, never manually maintained.

Modelling Isn't Enough, Good Design Is Still Required

Modellers take you x% of the way toward a solution, but you need a way to add the remaining (100-x)% without modifying generated code. The answer is Design Patterns—specifically the "open to extension, closed to modification" principle. Good design allows us to generate software components that can be extended externally through Strategies, Template Methods, Decorators, Composition, and Chain of Command patterns, rather than internally through code modification.

It's unfortunate that code-generation fell out of favor just as Design Patterns made it truly practical.

Every time we thought good design didn't matter "because it's just generated code," we were proven wrong. We'd run into problems that forced us back to fix the underlying design. Even code-reuse remains important—generating code doesn't eliminate the need for it. Web and mobile applications suffer from large download sizes and times if the underlying architecture is poor. You're far better off with a poorly designed modeller that produces well-designed systems than a well-designed modeller that produces poorly designed ones.

Since FOAM generates well-designed output and generates itself, the modeller and the systems it produces maintain equivalent quality.

Fine-Grained Components

Rather than generating large monolithic components or systems from Models, we generate many small, fine-grained components designed to work together while remaining individually replaceable, augmentable, and recomposable—more like Lego bricks than diecast toys.

The challenge with fine-grained components is composition: you end up with many small pieces that must be assembled into a working system. We solve this in two ways.

Context-Orientation provides implicit hierarchical dependency management, dramatically reducing explicit composition boilerplate. Facades create single components that hide the complexity of many smaller ones. For example, FOAM's EasyDAO composes many common DAO (Data Access Object) Strategies and Decorators into a working composite.

Strategies handle data storage: local-storage, indexedDB, MongoDB, REST servers, simple arrays. Decorators add functionality: sequence number assignment, GUID assignment, logging, profiling, validation, authentication, caching. Not all combinations make sense and some conflict, but EasyDAO handles this complexity transparently.

Augment, Don't Replace

We don't try to model 100% of the solution. A 90% solution is perfectly acceptable (in Java, we typically generate 80-98% of code), with developers coding the rest in their target language. Much of the custom code is just a few lines—pre/post property setters, custom validation, method bodies. This code is part of the model, embedded in our DSL rather than the other way around. We call this an "inverted internal DSL."

Our mLang DSL for database queries, by contrast, is a regular internal DSL.

For specific domains, we've achieved 100% solutions. The Chrome App Builder, written in FOAM, generates ChromeOS Kiosk and Digital Signage apps entirely code-free. It's popular with >70k users and currently generates ~20% of all new Chrome apps.

Strong Canonical Interfaces

FOAM reuses a small set of canonical interfaces heavily. Developers implement, decorate, or compose these few: DAO, View, Validator, Authenticator, Agent, Action, Comparator, Adapter, Factory, Parser, Sink, and Predicate. Many implementations behind fewer interfaces.

Feature-Oriented

Best to watch the [video](https://www.youtube.com/watch?v=n699DWb2TUs) for this one.

Active Models

FOAM objects retain a reference to their Model at runtime—"Active Models." This enables data-driven programming: code that reflects on or interprets an object's Model at runtime to provide functionality for any modelled data. This is the main alternative to code-generation, though FOAM supports both. Examples include generic DetailView, TableView, various DAOs, JSON and XML adapters.

Example:

> var john = Person.create({fName: 'Jonathan', lName: 'Edwards'});
> john.toJSON();
"{
   "model_": "Person",
   "fName": "Jonathan",
   "lName": "Edwards"
}"
// Reference any object's model via the model_ property:
> john.model_.toJSON();
"{
   "model_": "Model",
   "id": "Person",
   "name": "Person",
   "properties": [
      {
         "name": "fName"
      },
      {
         "name": "lName"
      }
   ]
}"

Modelled Model

FOAM's Meta-Model is its own Model. Normally, models are described by a meta-model, which may be described by a weaker meta-meta-model, creating an endless descent until it's no longer worthwhile. FOAM avoids this spiral by looping back on itself:

> foam.lang.Model.model_.id;
'foam.lang.Model'

This is enabled by FOAM's clever bootstrapping technique using refinements: starting with a simple model and incrementally improving it in place.

FOAM's self-bootstrapping approach is the primary reason why it remains so small and uniform.

Code Is Data, Really

Everyone says "code is data," but very rarely is it truly first-class data. FOAM Models are modelled, so you can do anything with them that you do with other modelled data: display in an MVC View, store in a DAO, query with mLang, represent in various languages, convert to XML or JSON, send across a network. Meta-programming becomes just regular programming. A refactoring tool applies an mLang to a DAO of Models exactly as an accounting application applies an mLang to a DAO of accounts receivable.

MVC Works on Code (If It's 1st Class Data)

MVC excels at creating applications that view or edit data. Multiple views of the same data stay synchronized—updates to one reflect in all others. You can simultaneously view/edit a Model in JSON, XML, graphical, and UML views, or create entirely new ones.

Functional Reactive Programming

FOAM makes extensive use of FRP for animations, physics, live-coding, and generally avoiding callbacks while simplifying MVC. We're very happy with this feature, though it's mostly orthogonal to the modelling aspects.


Note: Several points above express different facets of the same core philosophy: designing systems where models are first-class, generated code is ephemeral, and good design remains essential regardless of automation.

Clone this wiki locally