5 ms·
Consider this tree: dog bark softly loudly run fast slow cat meow purr run Let's call th
by javajosh 11y ago
Consider this tree:
dog
bark
softly
loudly
run
fast
slow
cat
meow
purr
run
Let's call this the coordinate-system of your API (I've extended it a little hopefully to make it clearer). It's a small coordinate-system because it doesn't take any strings, but whatever. 'loudly' only makes sense in the context of 'bark', and 'bark' only makes sense on the context of 'dog'. That list of nodes is a coordinate that identifies a unique position in the API. (Note that in this API, actually just "loudly" should be enough to call the right code, but that won't be true in general.)
To be formal you can pick out the code by saying `(do dog bark loudly)`, where `do` is a reduction that concatenates and executes the code at each node. (With a little constraining you could say `(do-special loudly)`)
OOP just calls these API coordinates by special impressive-sounding names. The top level "objects" and the second level "methods" and the third level "method arguments", and imposes all kinds of finicky constraints (some of which are even useful). Languages differ wildly with these constraints: Java doesn't let you have a unique instance method (well, not without either reflection or bytecode manipulation, anyway), but Ruby does (lovingly called "monkey-patching"). And it's quite rigid in it's current form, with a fixed noun/verb/adverb structure.
At the end of the day all that detail obscures the simple truth that your code defines a coordinate system and an invocation is a single coordinate. There's a lot more to be said about this, of course! But I hope it helps/inspires.
- rdtsc 11y agoAccording to Alan Kay true OO is message based. First you'd identify what the objects are -- it seems dog and cat. Then you have a choice. Can either send them messages which look like {bark, softly}, {bark, loudly}, {run, fast}, {run, slow}. Similar for cat. Or can think of dog having an internal state machine and can be in two states -- barking or running. When it is barking he can get two messages {softly} or {loudly}. If it is running, can get fast or slow messages. There is a way to switch from one state to another say getting a {bark} or {run}. Other messages might lead to an error/exception. This way the first way of doing things is just sending a message which both switches state and regulates intensity of activity. Heck pretty much a similar example is presented in Learn You Some Erlang For Great Good book in the "Rage Against the Finite State Machine": http://learnyousomeerlang.com/finite-state-machines http://learnyousomeerlang.com/finite-state-machines (My favorite state machine is a that of a cat though in that text)
- javajosh 11y agoYes indeed. But what I'm saying is that there is actually just one nameless state machine whose coordinate system is organized as a tree who's leaves represent the machine's degrees-of-freedom. It's kind of an odd thing: the leaf both partitions state, and it also co-locates the DoF for changing the state (or at least it's apparent state, if you're immutable). Neat link, I'll have to check it out. I've not done erlang but I'm quite intrigued by gen_server in particular. Sorry for talking about coordinate-systems: I can't help it, I have a physics degree.
- sparkie 11y agoThis is good intuition. I tend to use this kind of idea in code these days. Here's an example: ($define! tree ($branch (dog ($branch (bark ($branch (loudly ($lambda () "WOOF!")) (softly ($lambda () "woof")))) (run ($branch (fast ()) (slow ()))))) (cat ($branch (meow ($lambda () "meow!")) (purr ()) (run ()))))) ($walk tree ($walk dog ($walk bark (loudly))) => "WOOF!" If one attempted to just say (loudly), an error would be raised because loudly only exists in the context of bark, which only exists in the context of dog, etc. We walk through the tree to find this context, then run the function (loudly). In the above Kernel code, the context is called an environment, and environments are first class. The evaluator takes an expression o and an environment e as arguments, and it is said that "o is evaluated in e". The tree is basically described by construcing new environments, where for example, the symbol "dog" is bound to another environment containing the bindings "bark" and "run". I've used the word "$branch" in the example for simplicity, but this term already exists in Kernel under another name. ($define! $branch $bindings->environment) $walk is implemented by combining the current environment with the one specified as its first operand, then the second operand is evaluated in the combined environment. ($define! $walk ($vau (env expr) dynenv (eval expr (make-environment (eval env dynenv) dynenv)))) Programming this way can be pretty fun, as you're not restricted by some arbitrary "impressive sounding names" for accessing environments in restricted ways. You can do things the way you want, and it's fairly trivial to implement your own object systems. I've termed it environment-oriented-programming. I basically use it as a means to implement OOP, generics, records, algebraic data types and whatnot. When combined with the use of other Kernel features like encapsulation types, it can be used to implement interesting type systems.
- javajosh 11y agoCool beans, sparkie. I'm working on something involving this shape (which is like a set of related call chains). The ability to call "loudly" by itself is a convenience only, a kind of syntactic sugar. I'm sure you could easily write the function that takes this tree, the string 'loudly' and emits '($walk tree ($walk dog ($walk bark (loudly)))'. :)
- 11y ago