[HN Gopher] The world's loudest Lisp program to the rescue
___________________________________________________________________
The world's loudest Lisp program to the rescue
Author : kryptiskt
Score : 86 points
Date : 2024-05-02 07:50 UTC (15 hours ago)
(HTM) web link (blog.funcall.org)
(TXT) w3m dump (blog.funcall.org)
| nemoniac wrote:
| Is it really established wisdom that multiple inheritance might
| be an anti-pattern? Anyone care to elaborate?
| nvy wrote:
| Isn't it the ambiguity of the Diamond Problem? Suppose B and C
| are both children of A, and D is a child of both B and C.
|
| If B and C both have methods foo(), which gets called when you
| do d.foo()?
|
| Seems like a real footgun requiring extra effort to avoid.
| phoe-krk wrote:
| In CL's solution, the order of superclasses matter to avoid
| ambiguity. If D is defined like (defclass d (b c) ...) then a
| method specialized on B is called; if like (defclass d (c b)
| ...) then it's otherwise.
| pfdietz wrote:
| And sometimes more than one method is invoked, using a
| sophisticated method combination infrastructure.
| phoe-krk wrote:
| Right, I assumed the default method combination, and also
| the simplest case of it with no around/before/after
| methods being defined... Golly, CL object system is
| complicated, now that I look at it from this perspective.
| tmtvl wrote:
| It's simple when you want it, and powerful when you need
| it.
| jerf wrote:
| In this case the problem becomes that while one can define
| a 100% consistent, coherent order for the compiler to use,
| the _human 's_ ability to understand what will happen when
| they call a method of a particular name, and also what that
| resolution method will do as the code is refactored and
| changed over time, exceeds anything a human can be
| reasonably expected to have.
|
| Really, all the problems with multiple inheritance are that
| the humans can't handle the complexity that results. The
| compilers can be made to do "something" that is arguably
| sensible.
| mark_undoio wrote:
| I think the implementation in C++ put it out of fashion, as
| later languages (e.g. Java) deliberately restricted it to avoid
| the complexity. The main criticism I saw was the potential for
| (variants of) the "diamond" where A is subclassed by B and C,
| then both of those are subclassed by D. Does D get two copies
| of A's state? It's hard to come up with an intuitive behaviour.
|
| More recently, the move seems to be away from class based
| object orientation (including inheritance) entirely.
|
| On the other side of things, I've never heard people talk about
| Python's multiple inheritance with the same tone used for C++ -
| but then there are cultural differences in the language
| communities too.
| fiddlerwoaroof wrote:
| Something I've found interesting is that most widely-used
| class-based inheritance languages eventually added multiple
| inheritance of implementations back in: PHP added traits that
| can contain method implementations; Java added default
| implementations on interfaces; etc.
| lmm wrote:
| The famous "super considered harmful" post (
| https://fuhm.net/super-harmful/ ) pointed out the key
| problem with diamonds, and it's mainly a problem with
| constructors. Allowing mixins that can have method
| implementations but only allowing one class parent with a
| constructor is a pretty good spot in the design space, and
| is what a lot of languages have converged on.
| fiddlerwoaroof wrote:
| I like CL's solution to constructors which is basically
| "specialize this generic function (SHARED-INITIALIZE or
| INITIALIZE-INSTANCE) with an :AFTER method". You reliably
| run all the initialization code for each class involved
| and you don't have to remember to call CALL-NEXT-METHOD
| (CL's spelling of super)
|
| Edit: I see that post refers to Dylan, which is more like
| CL than python in the important ways. IMO, sleeping on
| CL's object system CLOS was a huge mistake of the
| "Java/C++ era" of our industry.
| bitwize wrote:
| > Does D get two copies of A's state? It's hard to come up
| with an intuitive behaviour.
|
| C++ gonna C++, which means the language covers _all_ the
| bases because some programmer might get mad if their use case
| wasn 't accounted for.
|
| C++ has something called virtual inheritance, wherein if
| subclasses B and C inherit _virtually_ from A, any subclasses
| of both B and C will get one copy of A 's state. Otherwise,
| they will get two copies: one from B and one from C.
|
| This solves the problem of addressing concerns of all
| programmers w.r.t. the diamond inheritance problem, but makes
| the language more complex (and triggers my CPPPTSD).
|
| https://en.wikipedia.org/wiki/Virtual_inheritance
| pfdietz wrote:
| A nice pattern from Common Lisp is to inherit the parts of an
| object from different superclasses. Method combination means
| one can write methods for those superclasses and then have them
| automatically combined in a subclass.
|
| Example: if one has tree nodes with various slots that
| represent children and you want to write a tree traversal
| function, you put each slot in a superclass, inherit from those
| superclasses in the correct order, and then write a method for
| each superclass that calls the child at that slot. The methods
| are combined in the right order automatically in a PROGN method
| combination.
| jolt42 wrote:
| Meh. Probably a reaction to getting "burned" by it. But show me
| something you can't get burned by.
| copx wrote:
| 90s-style Java OOP showed everyone that heavy use of multiple
| inheritance is the worst thing since 80s-style BASIC where ever
| third line was a GOTO.
|
| Imagine one class inheriting from 50 other classes through
| multiple inheritance..
|
| People really used to construct classes like:
|
| "Iron Sword inherits from Iron which inherits from Metal which
| inherits from Meltable (which inherits from Temperature) and
| Material. But of course it also inherits from Sword which
| inherits from Weapon and Edged. Meanwhile Weapon inherits from
| Equipment which inherits from Ownable and Item which.." and so
| on.
|
| Basically you make every aspect and attribute of an entity a
| class and then create your entity's class by mushing together
| all those classes through multiple inheritance. The results
| are..not pretty.
|
| Such code quickly becomes very hard to comprehend and maintain.
| mikepurvis wrote:
| Yup. No amount of generated documentation or static analysis
| can make up for the cognitive load required to reason about
| where a particular method is actually being dispatched to
| under those conditions.
| Jtsummers wrote:
| 90s Java did not have multiple inheritance (nor does today's
| Java). It did have multiple interfaces, but they only carried
| a spec of the interface and no implementation details beyond
| that. C++ was the one with multiple inheritance, if you are
| trying to reference a popular 90s OO language.
| anthk wrote:
| OOP would work fine for a text adventure, such as Inform6
| against the Z-Machine, which pretty much the gameplay
| rooms->objects it's perfect for this. For everything else...
| well... maybe just CLOS it's usable enough.
| bitwize wrote:
| 90s Java didn't do that because Java doesn't support multiple
| class inheritance.
|
| 90s C++, however, did.
|
| Funny you should cite a game example. I once read about how
| the developers of StarCraft[0] ran into the same Goddamn
| inheritance problems I did when trying to build a custom game
| engine and a game with that engine. Adding behaviors via
| inheritance _seemed_ like a good idea _at the time_ (mid-late
| 90s), especially given all the propaganda we read from our
| C++ compiler manuals and such. But it turned into a situation
| where you either accepted multiple inheritance with all of
| its complexity and suck, including "which of the multiple
| base classes that implement 'foo' do I want when I call
| derived::foo()?" -- or resorting to delegates or other
| methods of composing behavior.
|
| Me, for gaming, I became an ECS convert and haven't looked
| back. There are some pain points when writing a game in ECS
| style... but the advantages pay for the relatively minor pain
| many times over.
|
| [0] https://www.codeofhonor.com/blog/tough-times-on-the-road-
| to-...
|
| CFlingy is a particle spawner. Why does that have to be in
| the inheritance chain, instead of a trait you _add_ to an
| object?
| mark_l_watson wrote:
| Great writeup! I am a long time user and fan of Common Lisp, and
| this is one of the more interesting use cases I have seen!
| varjag wrote:
| Thank you Mark! There are blessed and cursed projects out
| there, and this one has definitely been the former.
| varjag wrote:
| Author here, if you have any questions.
| anthk wrote:
| On Common Lisp, I loaded a nearly 30 yo eliza Chatbot written in
| CL, it ran almost straight under SBCL with just omitting an
| error:
|
| https://www.cs.cmu.edu/afs/cs/project/ai-repository/ai/areas...
| sbcl --load eliza.lsp (top-level) (hello
| how are you)
|
| Do not use punctuation. Use (goodbye) to exit.
|
| From a Unix user like me, SBCL/CL looks a bit bloaty and non-
| Unix, but I have to acknowledge that CL and Emacs' Elisp had a
| great history on compatibility and easyness due to the
| homoiconicity. In plain English: everything it's handled in the
| same way everywhere. The syntax will be the same on every
| function.
___________________________________________________________________
(page generated 2024-05-02 23:00 UTC)