
The Universal Design Pattern - mqt
http://steve-yegge.blogspot.com/2008/10/universal-design-pattern.html
======
silentbicycle
While he is focusing on Javascript and only makes a passing reference to Lua,
it's worth noting that it has a very flexible and efficient implementation of
property maps (called "tables") as its primary abstract data structure. You
can quite easily set up inheritance behavior for prototyping, along with read-
only tables and several other things, by defining access hook functions in
"metatables" (property tables for the tables themselves).

See also: Chapter 16 (OO) in _Programming in Lua_. The first edition is
available online: <http://www.lua.org/pil/16.2.html>

~~~
jcromartie
Indeed. Lua lets you do practically anything you want, because it only gives
you the building blocks of an object system.

~~~
silentbicycle
It's not an elaborate undertaking, either: you can make an object system in
just under a page of code.

Here's a good example, with lots of commentary:
<http://www.erix.it/progutil/gpeddler2/help/oo.html>

------
andreyf
_...none of these modeling schools is "better" than its peers. Each one can
model essentially any problem. There are tradeoffs involved with each
school..._

Who are you and what have you done with Steve Yegge!?

 _The Pulitzer Prize it won doesn't nearly do it justice. It's one of the
greatest and most unique works of imagination of all time._

Ah, there he is :)

------
amix
A thing I have noted is that every time I code in Java I feel handicapped for
the lack of proper syntactic sugar for maps (I mostly code in JS/Python where
maps are used for _all_ kinds of things (and hacks)). Anyway, great post.

~~~
silentbicycle
Ditto with Lisp, actually. Maybe something good could be done with read
macros, but |(table 'key) -> (lookup table 'key) just doesn't do it for me.
Any ideas? Lisp has alists, but (as noted elsewhere) they search in linear
time.

Python, Lua, and JS all have syntactic sugar for maps, and I think it makes a
really big difference. They're quite versatile. Lua's table.key ->
table["key"] sugar for all string keys is particularly nice.

~~~
gcv
Don't know if this will do what you want, but these two reader macros add
syntactic sugar for Common Lisp's aref and gethash forms. You should put these
definitions in an eval-when. Disclaimer: I haven't used these in a while (not
programming in Lisp at the moment, alas), so they may have some limitations
which I have forgotten.

    
    
      (set-macro-character
        #\] (get-macro-character #\)))
    
      ;; Array syntax: [array-name array-references] is equivalent to
      ;; (aref array-name array-references)
      (set-macro-character
        #\[
        #'(lambda (stream char)
            (declare (ignore char))
            (destructuring-bind (array-name &rest array-references)
                (read-delimited-list #\] stream t)
              `(aref ,array-name ,@array-references))))
    
      ;; Hash table syntax: #[hash-table hash-key &optional default] is
      ;; equivalent to (gethash hash-key hash-table &optional default)
      (set-dispatch-macro-character
        #\# #\[ #'(lambda (stream subchar arg)
                    (declare (ignore subchar arg))
                    (destructuring-bind (hash-name hash-key &optional (default nil))
                        (read-delimited-list #\] stream t)
                      `(gethash ,hash-key ,hash-name ,default))))

~~~
silentbicycle
Actually, would there be any real problem with keeping the map in a closure,
and passing around a function that gets (or, with a second optional argument,
sets) the value for a given key?

    
    
      (h 'key) and (h 'key 'new-val)
    

That's not too bad syntax-wise. Or, taking a cue from actor languages:

    
    
      (h 'get 'key) and (h 'set 'key 'new-val)

------
streblo
Every time I read one of Steve's blog posts, I realize how little I know about
software development.

~~~
silentbicycle
Here are a couple more links with good discussion about prototype-based
programming:

* <http://www.c2.com/cgi/wiki?PrototypeBasedProgramming> * <http://www.c2.com/cgi/wiki?ClassesPrototypesComparison>

SY is mostly talking about prototype-based programming in terms of Javascript,
but it is also well-supported in some other languages, especially Lua (The
chapter on OO in _Programming in Lua_ is IMHO the clearest explanation of
prototypes I've found.), and it's probably possible to implement an
inefficient-but-good-enough-for-brainstorming version in most languages.

~~~
dhotson
Thanks for the links... I love the old c2 wiki.

There are some real gems in there. :)

~~~
jcromartie
It's only old in the sense that it has been there for a long time. It's still
active and updated frequently.

------
pchristensen
Hooray! Some of my faith in Hacker News is restored. Every time I see a Yegge
posted with a couple comments, I'm afraid that they will be complaints about
how long it is. This time, they're about the article. Thank you for keeping it
positive!

~~~
Sujan
but well, it is looooong, isn't it?

;)

~~~
pchristensen
Be grateful:

 _"I considered splitting it into 3 articles, but instead I just cut about
half of the material out. (Jeff and Joel: seriously. I cut 50%.)"_

------
phaedrus
You should look at "Io," a language based entirely on this modeling
philosophy: <http://www.iolanguage.com/>

I have to disagree with people who say class based modeling is "intuitive". It
only seems intuitive after you've been steeped in OO practices for years. (It
certainly isn't intuitive to many first or second year CS students!) But
prototype based modeling, on the other hand (prototype modeling is Io's term
for yegge's Universal Design Pattern) is something that I think _is_
intuitive, because there are no classes, only objects, and making a new object
is just a matter of cloning an object you already have and adding to it.

~~~
arockwell
What I have always thought is dangerous is that class modeling only seems
intuitive when discussing everyday concrete objects. Your average CS student
does not have a problem understanding that a Car has an engine and 4 wheels
and is a type of automobile. However, when modeling more abstract concepts
getting the design correct is usually very nuanced.

------
nihilocrat
That's a neat post, the talk about Wyvern gets me really excited because game
programming is my primary domain of interest.

Someone please enlighten me; is the property model pretty much just reverse or
non-hierarchical OO? Instead of saying "J.T. inherits from football player"
you say "football player doesn't really exist, J.T. just inherits from a bunch
of objects which are instances that have the properties of a football player"?
The difference here is that inheritance is more like a node graph and a not
top-down hierarchy, where the only real root object is the super-generic
"object" object.

If this is true, what about the "Chieftain" example Yegge said he left out? Is
there a prototype called "Chieftain" which objects can inherit, but which
alone doesn't fully implement a game object? Is it the case that you can't
have "Chieftain" objects running around, as they only have all the properties
needed when they are, for example, an "Orc Chieftain"?

~~~
groby
If game programming is your interest, you might want to also look at Scott
Bilas presentation for a "data-driven object system":
<http://www.drizzle.com/~scottb/gdc/game-objects.ppt>

They implemented pretty much what Steve is describing for Dungeon Siege.
(Presentation was in 2002, so they started on it probably around 2000. Take
that for "no earlier references", Steve ;)

I vaguely recall that Thief used a similar system, too. There's also at least
on "NextGen" game that uses that system that I'm aware of. It's pretty
powerful (and makes it incredibly easy to shoot yourself in the foot)

As for the "Chieftain" example - it's usually solved by providing multiple
prototypes for an object. With all the ensuing uglyness.

~~~
nihilocrat
Thanks for the link! I actually saw something almost exactly like this by
someone who was working for Looking Glass, but it didn't go in depth about the
scripting layer, it just mentioned that you could either go with "static"
classes, "data-driven" classes, or a hybrid. In my own development, I've
generally gone with hybrid just because the value of flexibility surpasses any
sort of performance hit, but I still keep some of the very simple stuff
(object position, velocity, etc.) in the static layer.

------
yters
Whoa, what a coincidence. I thought of using this for a major programming
project I'm doing, without any idea whether it was a good idea or not. I've
been having some serious doubts as I've been implementing it, so this article
gives me a sense of validation.

------
peregrine
I just bought GEB cause its the 3rd time I've been suggested to read it.

Good ol used books.

------
yummyfajitas
Just a note, there is a python library implementing something very similar to
this. It's quite nice, I use it regularly.

<http://code.enthought.com/projects/traits/>

------
cousin_it
The post reminded me of Will Thimbleby's MISC, an experimental Lisp-like
language with maps instead of lists.

Linky: <http://will.thimbleby.net/misc/> (Java alert!)

------
jwilliams
This is very informative... great post.

------
andreyf
_Unfortunately Emacs doesn't support any notion of prototypes._

Sure it does - just cons stuff onto assoc lists. I think PG mentioned this at
the end of his "Arc lessons" essay.

~~~
silentbicycle
True, but note that alists run in linear time. They're great for some things,
but you probably don't want one with thousands of entries.

You could use hashtables instead, but the syntax for hashtables in Emacs Lisp
is cumbersome.

~~~
palish
I've only had a small amount of experience with Lisp, so I'm just curious:
would macros solve syntactic problems like that?

~~~
silentbicycle
Not really. There isn't really any major reason why you'd need to wrap it in a
macro, because it's not enough code to be worth running at compile time, and
you don't need to control evaluation order.

The bigger problem in elisp is that it doesn't have any kind of _remotely
sane_ namespacing, so instead of being something like Hashtable.make and
Hashtable.set (which you could alias to HT.make and HT.set if you like),
functions are either scattered all over the one global namespace or are _all_
prefixed with cumbersome-package-name-. You could alias them all individually,
but at that point it really doesn't contribute much to readability.

The potential solution probably involves read macros, which (for example)
convert 'STATE to (quote STATE) _at read-time_. Having read macros that could
convert e.g. x[23] to (nth 23 x), x['key 'val] to (puthash 'key 'val x), etc.
would be quite enough, but read macros are prefix, and [23]x really doesn't do
it for me. (I'm experimenting with adding postfix read macros to a Scheme-ish
interpreter of mine, but I haven't found a good way to make it fit the rest of
Lisp comfortably yet.)

------
dmoney
Perhaps Greenspun's Tenth Rule ("any sufficiently complex C program contains
an.. implementation of half of Common Lisp") applies as well to Java programs
containing JavaScript. The question is whether Lisp and JavaScript contain
each other.

------
qqq
My comment on how this is related to epistemology:

<http://curi.us/blog/post/1339-steve-yegge-on-epistemology>

------
mjnaus
Damn, did someone actually read this whole thing?

