The Intellectual Wilderness There is nothing more useless than doing efficiently that which should not be done at all.

2020.09.24 11:02

Erlang: Dispatching Closures (aka: why we don’t do OOP)

Filed under: Computing — Tags: , , , , , — zxq9 @ 11:02

Oh my! It’s like “Creating nouns in the kingdom of verbs“!
(The link above is to a great post from Steve Yegge — read it and come back if you aren’t already familiar with it.)

A video talk-through of the code below and a bit of discussion

I wrote a funny little module today to demonstrate to a friend why FP folks sometimes refer to OOP objects as “dispatching closures” and occasionally make quips like “objects are a poor man’s closures”. I thought it would be fun to share it and also wanted a reference page to which I can refer people who have questions because this comes up every so often when a new OOP programmer starts pondering the nature of the universe after reading some introductory FP material.

Quite a lot of extra functionality could be added to this, such as distinguishing between functions that update the state of Self and functions that don’t to alleviate the annoyance of having to ignore the NewSelf return element of any “methods” beyond the pure built-in ‘get’, but we can leave that for another day as I really don’t expect anyone in their right mind would use the module below in real world code — it just introduces complexity, mental overhead, and potential weirdness if you pass an object between two different processes (send it to a process on another node and boom!), and in Erlang funs really shouldn’t be all that long-lived anyway.

In the below code a syntactic rewrite is needed to understand how to call the methods:

Pseudo PythonOOPsy Pseudo Erlang
class MyClass:
# stuff
MyClass = class(Data, Methods)
my_object.data_elementMyObject({get, DataElement})
my_object.data_element = valueMyObject({set, DataElement, Value})
my_object.do_something()MyObject({do, Something})
my_object.update(stuff).do_something(){ok, NextObject} = MyObject({do, update, Stuff}),
NextObject({do, something})

Here is the definition bit of the code, there is also a slightly more extensive and commented snippet on GitLab that has an additional demo/0 function that demonstrates one way dispatching closures can be called, manipulated, and extended through inheritance:

-module(oop).
-author("Craig Everett <zxq9@zxq9.com>").
-export([class/2, class/3, demo/0]).


-record(s,
        {data    = #{},
         methods = #{}}).


class(Defaults, Methods) ->
    fun
        (data) ->
            maps:keys(Defaults);
        (methods) ->
            maps:keys(Methods);
        (Data) when is_map(Data) ->
            State = #s{data = maps:merge(Defaults, Data), methods = Methods},
            object(State);
        ({subclass, NewDefaults, NewMethods}) ->
            class(maps:merge(Defaults, NewDefaults), maps:merge(Methods, NewMethods))
    end.


class(Inherits, Defaults, Methods) ->
    Inherits({subclass, Defaults, Methods}).


object(State = #s{data = Data, methods = Methods}) ->
    fun
        ({get, Label}) ->
            maps:get(Label, Data);
        ({set, Label, Value}) ->
            NewData = maps:put(Label, Value, Data),
            object(State#s{data = NewData});
        ({add, Label, Method}) ->
            NewMethods = maps:put(Label, Method, Methods),
            object(State#s{methods = NewMethods});
        ({do, Label}) ->
            F = maps:get(Label, Methods),
            F(object(State));
        ({do, Label, Args}) ->
            F = maps:get(Label, Methods),
            F(object(State), Args);
        (data) ->
            maps:keys(Data);
        (methods) ->
            maps:keys(Methods)
    end.

While this is an interesting construct, it is absolutely insane that anyone thought it was so utterly and all-encompassingly important that an entire new syntax should be developed just to hide the mechanics of it, and further, than entire languages should be created that enforce that this is the One True Way and impose it on the programmer. It’s cool, but not that cool.

2013.08.13 15:58

Don’t Get Class Happy

Filed under: Computing — Tags: , , , , , , — zxq9 @ 15:58

If you find yourself writing a class and you can’t explain the difference between the class and an instance of that class, just stop. You should be writing a function.

This antipattern — needless classes everywhere, for everything — is driving me crazy. Lately I see it in Python and Ruby a good bit, where it really shouldn’t occur. I feel like its a mental contagion that has jumped species from Java to other languages.

In particular I see classes used as inter-module namespaces, which is odd since its not like there is a tendency to run out of meaningful names within a single source file (of reasonable length). Importing the module from outside can either import * from foo,or import foo, or from foo import filename, or even import foo as bar (ah ha!) and make flexible use of namespacing where it is needed most — in the immediate vicinity of use.

So don’t write useless, meaningless, fluffy, non-state&behavior classes. Any time you can avoid writing classes, just don’t do it. Write a function. Its good discipline to see how far you can get writing zero classes in an implementation, and then write it with some classes and see which is actually less complex/more readable. In any case writing two solutions will give you a chance to examine the problem from multiple angles, and that’s nearly always an effort that results in better code since it forces you to value grokness over hacking together a few lines that sort of cover the need for the moment (and forgetting to ever come back and do it right).

Or maybe I should be admonishing people to seek flatness in namespaces, since this implies not writing classes that are stateless containers of a bunch of function definitions which incidentally get referred to as “methods” even though they don’t belong to a meaningful object in the first place.

Or maybe I should be railing against the oxymoronic concept of “stateless objects/classes”.

Or maybe I should be screaming about keeping our programming vocabulary straight so that we can finally say what we mean and mean what we say. (Sound advice in all walks of life.)

This last idea is perhaps the most indirect yet powerful of all. But it implies “standards” and so far in my experience “standards” and “computing” don’t tend to turn out very well once we get beyond, say, TCP/IP or ANSI and Unicode.

Powered by WordPress