Nick Sieger: RailsConf: Steven Hammond - DSLs and Rails Pluginsdo what you lovetag:blog.nicksieger.com,2005:TypoTypo2007-08-31T16:41:59+00:00Nick Siegerurn:uuid:9bc768d6-878b-45b8-ab9c-995a570ee47a2006-06-24T16:18:00+00:002007-08-31T16:41:59+00:00RailsConf: Steven Hammond - DSLs and Rails Plugins<h2>Intro</h2>
<p>Steven talks on the whats and whys of domain specific languages.</p>
<p>Steven’s background is is as a java developer and more recently, a
project manager, or “sith lord” as he put it.</p>
<p>Some early exposure to DSLs for him involved the use of something
called Program Composition Notation that was used to describe a
weather modeling program to be run on massively-parallel
supercomputers.</p>
<h2>Features of DSLs</h2>
<ul>
<li>Usually a partial programming language focused on a single problem
domain.</li>
<li>Replicate syntax/structure that is common to users of that domain.</li>
<li>Tries to be as elegant, concise and expressive as possible in that
domain.</li>
</ul>
<h2>Horizontal vs. Vertical</h2>
<ul>
<li>Horizontal – programming problems (e.g., Rails migrations)</li>
<li>Vertical – business domain problems (e.g., a game DSL, insurance
product definition, circuit board specification)</li>
</ul>
<h2>Recipe example</h2>
<p>Steve contrasted a procedural, C-style recipe vs. a Ruby-based DSL
that used blocks to delineate ingredients from procedures. The
differentiating point was “which one could your mom translate into
meatloaf”? I suppose neither if you don’t like your mom’s meatloaf.</p>
<h2>Rails DSLs</h2>
<ul>
<li>The framework itself (“is the whale shark a fish?”)</li>
<li>Migrations</li>
<li>RJS templates</li>
<li>Plugins
<ul>
<li><code>acts_as_state_machine</code></li>
<li>RESTful Rails</li>
<li><code>game_dsl</code></li>
</ul></li>
</ul>
<h2>When to create a DSL</h2>
<ul>
<li>established syntax or vertical domain</li>
<li>soemthing that’s inelegant in normal imperative or object-oriented
style</li>
<li>abstract implementation details so that you don’t depend on them
(e.g., migrations does this for database DDL)</li>
<li>reuse</li>
</ul>
<h2>DSLs and Reuse</h2>
<ul>
<li>“Reuse is vastly overrated” – DHH in 1/2006
<ul>
<li>Business logic is very application specific and is difficult to
reuse across projects</li>
<li>Level of reuse is at a lower lever where there is an information
barrier to understanding the component (e.g., LINPACK)</li>
</ul></li>
</ul>
<h2>Designing a DSL</h2>
<ul>
<li>Be careful to balance forward-looking with extraction. In other
words, DSLs should be extracted or driven by need just like anything
else.</li>
<li>Layout a domain syntax (not necessarily in Ruby)</li>
<li>Extract and evolve over time</li>
<li>Translate business requirements/statements into succinct programming
fragments. (e.g., rolling dice and assigning attributes)</li>
</ul>
<div class="typocode"><pre><code class="typocode_ruby "><span class="comment"># extending Integer to deal with die-rolling</span>
<span class="number">3</span><span class="punct">.</span><span class="ident">d6</span>
<span class="number">3</span><span class="punct">.</span><span class="ident">d6</span> <span class="symbol">:keep_best</span> <span class="punct">=></span> <span class="number">3</span>
<span class="comment"># Deck-dealing example</span>
<span class="keyword">class </span><span class="class">Tile</span> <span class="punct"><</span> <span class="constant">AciveRecord</span><span class="punct">::</span><span class="constant">Base</span>
<span class="ident">acts_as_deck</span>
<span class="keyword">end</span>
<span class="keyword">class </span><span class="class">Player</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
<span class="ident">acts_as_player</span>
<span class="keyword">end</span>
<span class="keyword">class </span><span class="class">DeckController</span> <span class="punct"><</span> <span class="constant">ApplicationController</span>
<span class="keyword">def </span><span class="method">deal_cards</span>
<span class="attribute">@deck</span> <span class="punct">=</span> <span class="constant">Tile</span><span class="punct">.</span><span class="ident">find</span> <span class="symbol">:all</span>
<span class="attribute">@deck</span><span class="punct">.</span><span class="ident">shuffle</span>
<span class="attribute">@card</span> <span class="punct">=</span> <span class="attribute">@deck</span><span class="punct">.</span><span class="ident">draw</span>
<span class="attribute">@deck</span><span class="punct">.</span><span class="ident">deal</span> <span class="symbol">:cards</span> <span class="punct">=></span> <span class="number">6</span><span class="punct">,</span> <span class="symbol">:to</span> <span class="punct">=></span> <span class="attribute">@players</span>
<span class="keyword">end</span>
<span class="keyword">end</span></code></pre></div>
<p>There was a discussion at this point about whether it was necessary to
use a DSL in this case, and Obie Fernandez piped in that if you’re
aiming for reuse or as an API, DSLs give a readable, understandable
representation that makes reuse and sharing easier.</p>
<p>Another comment was regarding namespace collisions, and Steven replied
honestly that there is not a good answer. If you combine a
game-playing DSL with a home-building DSL, are you randomizing your
porch?</p>
<p>YAML provides another good medium for succinct data layout (e.g.,
database.yml). Steven’s example followed the same pattern to apply to
selecting different sources for a random number generator.</p>
<h2>Pitfalls</h2>
<ul>
<li>DSLs should not necessarily be the first solution – the domain
could be too narrow or too broad.</li>
<li>Too many DSLs (or even plugin bloat) could end up compromising the
readability of the code. Namespacing comes in here. Which
DSL/plugin provided the given functionality?</li>
<li>Interaction side-effects – it’s not clear how mixing metaphors can
make the solution clearer.</li>
<li>Vertical DSLs additionally suffer from the problem that it’s still
programming, and there are syntax issues to overcome, even though
the DSL can faciliate interactions with the customer.</li>
</ul>
<p>A comment came from Aslak Hellesoy about the use of external DSLs
(i.e., built using parser generators). Steven replied that it is
appropriate in some cases, but the complexity will be higher.</p>
<p>It seems to me that in the case of external DSLs, the freedom (lack of
constraints) can actually be a burden. You can design syntax however
you want but you need to be sure that it makes sense to the people who
will be using it. Unless you’re really good with language and
interface design it can be easy to create a custom, niche language
that nobody knows anything about and is inflexible. In my experience
it’s hard to use parser generators like yacc to build flexible
parsers. Ruby turns out to be really good for DSLs because you can
omit details (parentheses and hash curly braces) and it still does
what you want.</p>