Skip to content

How FOAM is Different

Kevin Glen Roy Greer edited this page Jan 21, 2017 · 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 pay careful attention to the design of the software that we create.
  • We generate fine-grained components and then use contexts and facades to compose.
  • 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 gives live-coding.

Long Answer:

Code is a Liability

Modelling-tools/code-generators are used to generate you a lot of code, but then this code invariably needed to be edited to add additional or custom behaviour. After you generated the code you would load it up in your editor and search for the /* insert code here */ comments. While these kinds of tools got you off to a good start, you soon ended up with more code than you could maintain. Kind of like winning a luxury home in a lottery, but then not being able to afford the taxes. The problem with this kind of approach was that code needs to be maintained, so is actually a liability, not an asset. Code-generators were actually liability generators. The actual asset is the features that the code supplies. What you want is the features, not the code. The solution to this is to never generate any code which needs to be edited or checked-in. Generated code should be part of the build process or runtime, but never edited and never checked-in.

Modelling isn't Enough, Good Design is Still Required

The solution to the “extension without modification” problem, described above, is Design Patterns. Modellers only take you x% of the way, but you need some way to add the remaining (100-x)%. We’ve already said that modifying the code is a bad idea, but the “open to extension, closed to modification” feature of Design Patterns, is exactly what we need to solve this problem. The careful application of good Design (Patterns) allows us to generate software components which can be extended externally, through Strategies, Template Methods, Decorators, Composition, Chain of Command, etc. rather than internally by modifying their own code. It’s a shame that by the time Design Patterns came around to make code-generation practical, the idea of code-generation had largely been discredited and abandoned.

Every time that we thought good design didn't matter because it was just generated code, we turned out to be wrong and ultimately ran into problems which forced us to go back and fix the design. Even code-reuse is important, which you might not think because you’re just generating the code anyway, but then for web apps, and especially for mobile web apps, you end up with large download sizes and times (although, you should really be downloading your small models to the client and then expanding them there).

You’re much better off having a poorly designed modeller that produces well designed systems, than you are to have a well designed modeller that produces poorly designed systems. But since FOAM produces well designed output (IMO), and it generates itself, you’re ensured that the modeller and systems that it generates are of equivalent quality (at one level).

Fine-Grained Components

Strongly related to the previous point is the use of fine-grained components. Rather than generating large monolithic components or systems from Models, we generate many small fine-grained components. These small components are designed to be used together to form a working system, but you still have the option of replacing, augmenting, rearranging, or recomposing them in some different way. Kind of like the difference between getting a lego toy car instead of a diecast toy car. The lego gives you more reuse and customization options. One problem with fine-grained component models in the past, has been that you’re then required to do the work to compose the many small components into a larger system. We handle this problem in two ways: Context-Orientation and Facades. Context-Orientation is an implicit hierarchical dependency management method which greatly reduced to need for explicit composition. Facades create single components which hide the complexity of composing many smaller ones. For example, in FOAM we have an EasyDAO which is responsible for composing many common DAO (Data Access Object) Strategies (actual DAO implementations that store data) and Decorators (proxies which provide some additional functionality over top of a Strategy) into a working DAO composite. Strategies might be local-storage, indexedDB, MongoDB, a REST server, a simple array, etc., and decorators might be things like sequence number assignment, GUID assignment, logging, profiling, validation, authentication, caching, etc. Not all combinations make sense and some are mutually exclusive, but the EasyDAO takes care of this.

Augment, Don’t Replace

We aren’t trying to model 100% of the solution. We’re perfectly happy for a 90% solution (in practice, in Java where you can count the lines of code, we typically generate between 80-98% of the code), and then let you code the rest in your target language(s). A lot of the custom code is only a few lines of code, or even only a single line. This code is actually part of the model and includes things like pre and post property set functions, custom validation, method bodies, etc. When our DSL and target language code work together, but the code is embedded in our DSL, rather than the other way around, we call this an “inverted internal DSL”. Our mLang DSL for specifying database queries, on the other hand, is a regular internal DSL.

However, for some specific problems, we actually do come up with 100% solutions. The Chrome App Builder is written in FOAM and is used to Model and generate FOAM ChromeOS Kiosk and Digital Signage apps. This is an entirely code-free modeller, but only for a limited domain. (The app is very popular, with >70k users and currently ~20% of all new Chrome apps are being built with it.)

Strong Canonical Interfaces

FOAM has a small number of canonical interfaces that it reuses heavily. Many of these are generated for developers by FOAM, but most of what a FOAM developer does it implement, decorate, or compose these few interfaces: DAO, View, Validator, Authenticator, Agent, Action, Comparator, Adapter, Factory, Parser, Sink, and Predicate.

More implementations behind fewer interfaces.

Feature-Oriented

Best to watch the video for this one.

Active Models

FOAM objects retain a reference to their Model at run-time, which is why we call them “Active Models”. This makes data-driven programming easy. By data-driven, we mean code that that reflects on or interprets an object’s Model at runtime to provide some kind of functionality to any type of Modelled data. This is the main alternative to code-generation, which we also support. Examples include a generic DetailView, TableView, various types of DAO’s, JSON adapters, XML Adapters, etc.

Ex.

> var john = Person.create({fName: 'Jonathan', lName: 'Edwards'});
> john.toJSON();
"{
   "model_": "Person",
   "fName": "Jonathan",
   "lName": "Edwards"
}"
// You can 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 you have models described by a meta-model and that meta-model may be described by a smaller weaker meta-meta-model, and you might even have a meta-meta-meta-model, on until it no longer becomes worthwhile. FOAM avoids this downward spiral by looping back on itself.

Ex.

Model.model_ === Model; true

The trick to this is bootstrapping. We start with a simple hand-coded BootstrapModel and then the real (Meta)Model which is it’s own (MetaMeta)Model. We then use the BootstrapModel to compile the Model half-way and then use the Model to compile itself the rest of the way. We have a line of code which reads:

Model = Model.create(Model)

Which would be the equivalent of writing a C compiler in C and then using a bootstrap compiler to compile and replace itself with:

cc cc.c -o cc

That FOAM bootstraps itself in this way is the primary reason why it is so small and uniform.

Code is Data, Really

Everyone knows that code is data, but very rarely is it really first-class data. FOAM Models are modelled, so you can do anything with them that you could do with any other modelled data: display it in an MVC View, store it in a DAO, query it with an mLang (our internal-DSL for database queries), represent it in various languages, convert it to XML or JSON, send it across a network, etc. Meta-Programming now becomes just like regular programming. A refactoring tool would just apply an mLang to a DAO of Models in exactly the same way that an accounting application might apply an mLang to a DAO of accounts receivable.

MVC Works on Code (if it’s 1st Class Data)

A continuation of the previous point. MVC is a great design for creating applications which view or edit data. You can have multiple views of the same data and have updates to one reflected in all of the others. You can simultaneously view/edit a Model in JSON, XML, Graphical, and UML views, or create new views.

Functional Reactive Programming

If you watch the FOAM video you’ll see that we make extensive use of FRP for animations, physics, live-coding, and just generally avoiding callbacks and making MVC simpler. We’re very happy with how this feature has turned out, but it’s mostly orthogonal to the modelling features.

Some of the above points are really just different ways to say the same thing.