8 ms·
The world's loudest Lisp program to the rescue
- nemoniac 2y agoIs it really established wisdom that multiple inheritance might be an anti-pattern? Anyone care to elaborate?
- nvy 2y agoIsn'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 2y agoIn 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 2y agoAnd sometimes more than one method is invoked, using a sophisticated method combination infrastructure.
- phoe-krk 2y agoRight, 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 2y agoIt's simple when you want it, and powerful when you need it.
- jerf 2y agoIn 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.
- Jach 2y agoFortunately in Lisp the compiler is available at runtime! I mean, it's just not that bad. I believe the commercial Lisp IDEs will just show you relevant info much like, say, Java IDEs, but even with a free Lisp you can still ask for it so you don't actually need to wonder what will happen as you're looking at a line. You just ask. The worst part of Lisp vs. C++ on multiple inheritance, I think, where it can be more confusing for Lisp is that Lisp will just overwrite slots (fields) sharing the same name, whereas C++ will shadow them. On the other hand methods aren't owned by individual classes in Lisp, so you get multiple dispatch by default. Lastly the presence of :before / :after / :around methods, combined with multiple dispatch, make it pretty straightforward to achieve behaviors through mixins that require pretty complex contortions otherwise in C++. (Or Java.) The behavior of those "auxiliary methods" is straightforward to reason about. All :before methods run before the most specific primary method, in most-specific-first order, and all :after methods run after the least specific primary method, in least-specific-first order. I'm probably going to convince some people otherwise by giving some more specifics, but as a minor example, consider a silly "game object" style class. I can always ask any class (e.g. an asteroid), hey, what's your class precedence list? (closer-mop:class-precedence-list (find-class 'asteroid)) returns a list of class objects: asteroid, game-object, sprite, add-groups-mixin, cleaned-on-kill-mixin, standard-object, slot-object, and T. From the source code where the defclass is, only game-object is shown. If you look at game-object, only sprite and the two mixins are shown as an example of multiple inheritance. I don't need to call that function to get the info either, it's readily available by calling 'describe on the class. (I think even free editors like Lem or emacs can be configured to automatically show the description of things if you hover over them, I just type ,s in vim.) The description includes the same class precedence list info, tells me the direct superclasses, any subclasses, direct slots (fields directly defined on the class), inherited slots... If I'm wondering what could happen if I call #'kill on an asteroid before I actually call it, I can ask with the built-in 'compute-applicable-methods function or 'closer-mop:compute-applicable-methods-using-classes, and it will show me the applicable methods are firstly the primary defined method, then an :after method due to the mixin. I can also compute the actual effective method that will be called with 'closer-mop:compute-effective-method. For something like #'kill, it shows what happens first is the primary #'kill method, then the :after method. For something like #'draw, let's say I overwrote the base implementation, now it shows there's just one method call, with the potential for the next base class method if the specialized method happens to use 'call-next-method. So in summary, the tools exist in various forms to wrestle the complexity and make it amenable to human understanding. Just like with tools such as cross-referencing, they help understand and create bigger systems, we don't have to limit ourselves to what can easily be done with physical code printouts and hand-made indexes.
- mark_undoio 2y agoI 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 2y agoSomething 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 2y agoThe famous "super considered harmful" post ( https://fuhm.net/super-harmful/ 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 2y agoI 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.
- deleted 2y ago[deleted]
- pfdietz 2y agoA 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 2y agoMeh. Probably a reaction to getting "burned" by it. But show me something you can't get burned by.
- copx 2y ago90s-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 2y agoYup. 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.
- deleted 2y ago[deleted]
- Jtsummers 2y ago90s 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 2y agoOOP 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.
- cess11 2y agoThe MUD-family of games are usually built in a C-like OOP-language, LPC. I think it's rather nice.
- dangmumwhore 2y ago[flagged]
- mark_l_watson 2y agoGreat 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 2y agoThank you Mark! There are blessed and cursed projects out there, and this one has definitely been the former.
- emptybits 2y ago"Long time user and fan" is an understatement. Thank you, again, Mark. I was re-reading your Loving Common Lisp book just an hour ago! I read elsewhere that you're also a Racket user. I'm curious ... aside from CL legacy code requirements, do you view Racket (the language and/or ecosystem) as a smart long term choice going forward with lisp projects?
- mark_l_watson 2y agoThank you for the kind words! I would choose one or the other for most of your Lisp dev. I have been just an occasional user of Racket forever, but in the last few years I have really been enjoying the language and the minimal tools I use for Racket dev. I also feel happy using Common Lisp, so it is difficult for me to make a definitive statement on preference. Racket and LispWorks Pro have portable UI libraries which is nice. I evaluated both Racket and LispWorks Pro for making standalone apps and they are both pretty good.
- varjag 2y agoAuthor here, if you have any questions.
- mtreis86 2y agoHow was working with posix threads? I've only dug into SBCL's various thread tools
- varjag 2y agoFortunately it was uneventful, as the idiom is the same as in any other programming language that support them. We used bordeaux-threads package for portability across the implementations.
- db48x 2y agoWhat does the evacuation alarm actually sound like? Does it reuse any of the sounds mentioned in the Tronstad study, or did you come up with your own?
- KennyBlanken 2y agoA ringing bell: https://youtu.be/MmrihxFhWJw?t=57 https://youtu.be/MmrihxFhWJw?t=57
- db48x 2y agoI might have guessed that there would be a youtube video! Thanks :)
- varjag 2y agoIt is a bell sound as the sister comment points out. We found that a multitude of sounds work with negligible difference in perception. The bell however was consistently voted the most comfortable in post trial questionnaire.
- guenthert 2y agoGiven that this is a safety-critical application, are condition/restarts being used? If so, what is your take on the value of those and can an example of restarts be listed? If not, have they been considered and if, can you share the reason not to use them?
- anthk 2y agoOn 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/classics/eliza/bg/eliza.lsp 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.
- troad 2y agoThis is a really cool story! Perhaps a slight segue, but I recently tried to learn CL for the first time and I was genuinely surprised by all the decades of accumulated cruft (mainly masses of semi-redundant and soft-depreciated standard library functions, with bizarre names). The way people talk about Lisp, I'd expected something more elegant. I suppose I should try something like Scheme or Racket, but it's hard to find an introduction to those that isn't bone dry. (Recommendations welcome!) I've also heard people say reading Lisp functions, inside out, ensconced (heya) in their parentheses, is somehow more comprehensible than sequential C style, but this state of enlightenment thus far eludes me. I can only speak for myself, but I definitely reason about code outside in rather than inside out.
- db48x 2y agoThe funny names all have history. They had history even at the time when Common Lisp was standardized.
- troad 2y agoNo doubt! I look forward to learning it in due course, but it's not exactly penetrable for a newcomer, particularly amidst a sea of parentheses.
- hickelpickle 2y agoLittle schemer is good, some people hate it some people love it. But it is a fairly light read the slowly teaches some syntax at a time, questions you about assumptions then revels the information as it goes on. It would be the least dry read. There is also sketchy scheme for a more thorough text, or even the rs7s standard, which are both pretty dry but short. What made me appreciate scheme was watching some of the SICP lectures (https://www.youtube.com/watch?v=2Op3QLzMgSY&list=PL8FE88AA54363BC46&index=1 https://www.youtube.com/watch?v=2Op3QLzMgSY&list=PL8FE88AA54...) and the little schemer to learn more. I also read some of the SICP along with it, though I put it down due to not having the time to work through it. Scheme is interesting and toying with recursion is fun, but the path a mentioned above is only really enjoyable if you are looking to toy around with CS concepts and recursion. You can do a lot more in modern scheme as well, and you can build anything out of CL. But learning the basics of scheme/lisp is can be pretty dry if you are just looking to build something right away like you already can in a traditional imperative language. But it is interesting if you are interested in a different perspective. But even RS7S scheme is still far from the batteries included you get with CL. I personal found the most enjoyment using Kawa scheme, which is jvm based and using it for scripting with java programs as it has great interop. I used it some for a game back end in the event system to be able to emit events while developing and script behaviors, I've also used it for configurations as well with a graphical terminal app, I used hooks into the ascii display/table libraries then kawa to configure the tables/outputs and how to format the data.
- worthless-trash 2y agoI would love to read more on these topics. I keep getting told that lisp isn't "used anymore" (Even though I actively do).
- justneedaname 2y agoThis reminds me of the very first project that I worked on, a warning system for rail trackside workers. There had been numerous case studies of near misses, injuries and even fatalities. The system currently in place at the time was, unbelievably, two people (in the case of a bi-directional line) stood downline within earshot of the main crew. When they saw a train approaching they would blow whistles and wave a flag, the workers would then move out of the way until the train passed. Yeah I also couldn't believe that such an archaic system was still in use - this was in 2019 mind. The company in charge of managing the railway lines reached out to our company and a few others to have us tender on a new design to help protect workers and reduce near misses. Our research led us to an existing system developed by a company in Switzerland which we essentially planned to modify for our national network, as there are differences in how railway lines are signalled across different regions. It consisted of units that could be placed periodically downline of the work site and would alarm when a train was approaching by use of real-time train location data. The main issue we faced though was how to ensure an accurate reading that gave enough time to vacate the line whilst not being excessive, as research suggested workers may believe it to be a false positive if nothing approached after a couple of minutes. To understand why this is a difficult problem it first helps to understand how traffic within a railway line is managed. The railway network is split into what is known as blocks, these are discrete sections of track separated by axle counters. Without a train the two sections of track are electronically separate, when one passes over a circuit is completed and the train's position can know be known to that exact location at that exact time. However these readings are discreet, with resolution of the trains position only being as good as the number of axle counters present on the line. This results in some tricky estimating of when "impact" will happen. Train speed is another metric that can be used in conjunction but again you only know the speed read at the last axle counter, anything could have happened between the last reading and "now". In the end our solution was to assume maximum line speed and warn when this would be within 30s of the worksite. We created a demo that worked flawlessly and the client was visibly impressed. However they then wanted a proposal, cost and everything else for the next phase within 2 weeks - so we had to pull out as we weren't able to produce it. This was a real shame for me as I look back on this project with fond memories, one of the few projects where we were essentially left to figure it out. Already in my short career (<5y) I've been fortunate enough to work on some interesting projects and gain interesting stories to tell...