Explicating the survival of the Shell as default OSS UI



  • @tarunik said:

    Now, if it'd only stop getting its maths wrong...

    Excel is American, it doesn't have "maths" it has "math!"

    ... it also has the math ability of an average American.



  • Excel, foul though it is when it's (inevitably) misused, is a rather nifty dataflow programming language.

    It's not as good, powerful, intuitive, or fast as Quantrix, but it's not the same price, and it's kinda there by default on pretty much every Windows box delivered.

    @Keith said:

    Skoda are now supposedly quite good since they were taken over by VW, which somewhat ruins the jokes that I was brought up with.

    Yeah, I know. The Octavia has the same motor as Audi's A3.

    "Can I get a new hubcap for my Skoda?"
    "Sounds like a fair swap"



  • Why do Skodas have heated rear windows?
    So you can keep your hands warm while you push it.


  • Trolleybus Mechanic

    @tufty said:

    Excel, foul though it is when it's (inevitably) misused, is a rather nifty dataflow programming language.

    Yep - on both counts.

    Now that I think about it, if I were to go about building a general-purpose, problem-solving, programmable tool, I'd probably be looking at spreadsheets for inspiration. I'm not really sold on the "big piece of paper" paradigm (large sheets are a bitch to navigate), but they are pretty damn intuitive.


  • ♿ (Parody)

    @GOG said:

    I'm not really sold on the "big piece of paper" paradigm (large sheets are a bitch to navigate), but they are pretty damn intuitive.

    Well, that's the thing. Sometimes an easy to use tool isn't the best tool. Everything comes with trade offs. Ideally, there would be no trade offs.



  • Sadly, Resolver One no longer exists. There is https://github.com/pythonanywhere/dirigible-spreadsheet, though I've not used it.


  • Trolleybus Mechanic

    I'd probably be thinking along the lines of "smaller pieces of paper" - that is: an "object oriented" spreadsheet where you might treat individual calculations as reusable building blocks.

    (This idea was brought to you by not enough beer on a Friday evening.)


  • ♿ (Parody)

    @GOG said:

    (This idea was brought to you by not enough beer on a Friday evening.)

    I plan to start remedying that issue in a couple of hours.


  • Trolleybus Mechanic

    Got mine just now; waiting for right temperature.



  • @boomzilla said:

    Probably wasting our time solving problems that don't need to be solved instead of

    @blakeyrat said:

    writing software before educating themselves on what came before. The reason the state of the art never advances is because there's no "standing on the shoulder of giants" in our field, at all. Every so often you get lucky and someone's experience is actually paid attention to (like Fred Brooks' work), but 90% of the time it's completely forgotten only 10 years later and then some other "clever" developer comes along an re-implements the same already-rejected idea.

    CADTFY



  • Being a dumb 'Murkin, I am unfamiliar with Skodas. I gather than they are somewhere down there with the Reliant Robin (which I only know of from the famous TopGear video, admittedly) and the Ford Pinto? Or the legendary Renault model from the 1960s which had cardboard floors (the name escapes me at the moment)? If it is worse than the Lada (which admittedly is a tough little car 😉 ) or the Yugo, it must be indeed an epic fail, which makes me wonder why I haven't heard of this alleged car before.

    Filed Under: I had a Chevy Shitv... I mean, Chevette as a teenager, which even Pinto drivers got to laugh at




  • Discourse touched me in a no-no place

    @ScholRLEA said:

    in some ways my idea is just shifting the problem rather than solving it

    The biggest barrier, especially for non-technical users, is appreciating what is possible easily, what is possible with application of effort and resources, and what is outright impossible. I don't have a good answer to that other than to say that this is why using agile development techniques can help: showing them a prototype can help a user get a better idea of what they can ask for.


  • Discourse touched me in a no-no place

    @ScholRLEA said:

    Being a dumb 'Murkin, I am unfamiliar with Skodas.

    They used to be truly shit, but have improved hugely since the end of Communism.

    @ScholRLEA said:

    Reliant Robin

    What do you call the special twin-exhaust Robin with the sunroof option? A wheelbarrow.


  • Discourse touched me in a no-no place

    @GOG said:

    Now that I think about it, if I were to go about building a general-purpose, problem-solving, programmable tool, I'd probably be looking at spreadsheets for inspiration.

    The only problem with a general-purpose tool is that it's harder to make it do a specific task. Specialise it and it will do its specialism far better, but other tasks much less well.

    That applies whether the tool is software or physical.



  • @ScholRLEA said:

    Being a dumb 'Murkin,

    Please don't type that ever again.

    @ScholRLEA said:

    Filed Under: I had a Chevy Shitv... I mean, Chevette as a teenager, which even Pinto drivers got to laugh at

    Wait what? A Chevette? Pinto drivers? HOW THE FUCK OLD ARE YOU!

    I kind of assumed you were like fresh out of high school with the whole LISP obsession thing. How can you be both old enough to have known pinto drivers, and naive enough to think LISP fixes all problems? It doesn't add up!


  • Discourse touched me in a no-no place

    @blakeyrat said:

    the whole LISP obsession thing.

    LISP is what you get when you think that interface doesn't matter at all, just semantics. (Or RDF, which needs to burn in hell.) Interface does matter, but other things do too…



  • Lisp is where you end up when you go past the level of even Blakeyrat's cynicism, when you finally burst through to the ideal. Where nothing else exists, only the lambda. Indeed, it shouldn't have been λ, it should have been α-ω.

    Cynicism only comes back in again once people start trying to explain IO as monads.

    Meanwhile, Skoda was an eastern european (Czech, actually) car manufcturer which was one of the few to break through the Iron Curtain (the other "common" ones being Wartburg and, later, Lada and the VEB Trabant). Skodas were cheap, ugly, and unreliable. In fact, the only benefit they had over the Wartburg was not leaving a cloud of 2-stroke smoke behind you, and the only benefit over walking was that you didn't have to walk straight away.

    Now they're owned by VW, and they have Audi engines - they are, I hate to say it, good cars.



  • @blakeyrat said:

    Wait what? A Chevette? Pinto drivers? HOW THE FUCK OLD ARE YOU!

    I'm 45, which is old enough to remember when Lisp actually seemed primed to change the world. No, I never used a Symbolics or an LMI (my first computer was a used Apple ][+), but I recall how big a deal they seemed to ready to make - except that., like the Xerox Star (which I did get to try a few times), they were too expensive and too weird, and along came cheap DOS PCs and slightly more expensive Unix workstations, and everyone went with the cheapest thing they could, despite the fact that a $12k Apollo or Sun couldn't do a tenth what a Classic Mac could, never mind a Genera workstation, so the whole future seemed to fall apart. Read "Lisp: Good News, Bad News, How to Win Big" - not just "The Rise of 'Worse is Better'", which is just part of it - to get an idea of where things stood in the late 1980s for the Lispers.

    I was too young even in 1989 to really have gotten into it much; at the time, I thought Modula-2 was a big thing, actually. What hooked me on Lisp in the late 1990s was the sheer flexibility of it; tere are things you can do easily in Lisp which are virtually impossible in other languages.

    Blame SICP if you have to; I read the second edition when it first came out and was fascinated. Blame Hofstadter too, I suppose, as I grew up with GEB on my nightstand in high school and read a little of it almost every night. While he didn't talk about Lisp much per se in that book, other books of his did, especially Metamagical Themas, and the mathematical and symbolic themes he discussed seemed to be embodied directly in Lisp.

    Here's the thing: the one thing that people here bitch about in Lisp is the lack of syntax, and how that is a barrier to learning. BULLSHIT. It is exactly that lack of structure that makes it an excellent language for learning and teaching. How much time is wasted in your typical C++ or Java class going over the picayune details of the language's syntax? Your typical introductory course spends more time on the syntax than on actually programming. Often it takes two semesters to fit it all in.

    Scheme's syntax? I could teach it in an hour. If there were a library to the language worthy of the name, I could have novices writing real programs in a week. Common Lisp is a bit harder, because of the separate function/variable namespaces crap and a lot of other things that seemed like a good idea in 1986 that turned out to be less than great, but it is still easier to learn than most other languages if you are willing to give it a try.

    The same could be said about Smalltalk, I'll admit. It too is a very pared-down language, focused on it's paradigm and on getting the syntax out of the way of the programmer. I've never learned much of it, but from what I've seen, it could be taught in under a day.

    The real problem is that most intro classes are taught by second-rate professors, or more often than not, their grad students, and they rarely if ever really want to be there. It is easy to pick a book on Java - any book on Java - and teach out of the textbook verbatim, without even needing to understand the material yourself, because most of the time is spend on the details of how a for() loop works. Even Python, which is a great language for novices and experts alike, has this problem, though it is at least mostly self-consistent. The barrier to entry is higher than you think.

    Lisp's barrier to entry is minuscule, for the student. It's the barrier to the teacher that causes the problem, because you actually have to know your shit to get very far, and be enthusiastic about it. SICP and HTDP have terrible reputations as boring textbooks, but it is bored and boring instructors that are the real issue. Those books don't teach themselves, they require an instructor to participate actively in the learning. Few professors are that interested in teaching (as opposed to their real work of research) to bother, so they use the same approach they do with other books and it fails miserably.

    Watch the first few of the Abelson-Sussman Lectures and you'll see what it means for a professor to actually give a damn about their course. You might not like the language - I'm not going to to try to convince you that it is great if you don't think it is - but just watch how the two instructors go about teaching. The material is dry, they've deliberately hamstrung the sub-set of Scheme which they are using, and the technology is very obviously dated; but there, in 1986, they accomplished something in those videos that has stood the test of time. That is why I want to be able to use Lisp in real work someday.


  • Discourse touched me in a no-no place

    @ScholRLEA said:

    tere are things you can do easily in Lisp which are virtually impossible in other languages.

    I think you ought to be a little bit careful with such assertions. Not all languages are as limited as the ones you talk about.
    @ScholRLEA said:
    I thought Modula-2 was a big thing

    I'm one of the very few people anywhere to have actually used Modula-3. I don't encourage a revival.

    The Modula-3 course I took was one of the first where I observed just how thoroughly disorganised a teacher could be. The little sample GUI programs he asked us to write for the course assessment had the “interesting” property of being so large that the compiler would fill up the temp filesystem on the workstations we were using and cause the machine to crash (this was the bad old days before shared libraries were a universal thing). Yes, our glorious teacher — otherwise good to listen to, interesting to listen to and knowledgable — had forgotten to test whether the task was possible on the only machines that undergraduates had access to that could run the compiler on that program…

    Protip: static linking mixes badly with diskless workstations.



  • @dkf said:

    I think you ought to be a little bit careful with such assertions. Not all languages are as limited as the ones you talk about.

    The problem is the "things you can do" he's talking about are things like, "here's a complicated recursive math problem expressed in 27 characters!!!" and the "things you can do" normal people are after are things like, say, "build a REST API", "create a Access-like DB application", "write a video game".

    Java and C# are great for doing the things I want to do. I don't even know what kind of things ScholRLEA wants to do, but I'm guessing he'd have to do about 47,000 of those things before he got to the point of "build a REST API".



  • @blakeyrat said:

    The problem is the "things you can do" he's talking about are things like, "here's a complicated recursive math problem expressed in 27 characters!!!"

    Oddly enough, no. I don't have a lot of interest in the math side of things, and Lisp, believe it or not, really isn't for mathematics. Historically, it was used mostly for AI, which is about as mathematical as poetry. Symbol processing isn't my big thing either, but I certainly think Lisp is well suited for it.

    I am aiming at the sorts of things most people want to do. I want to make them simpler, and more robust, which means (among other things) reducing a lot of the boilerplate that dominates most code in the majority of current languages. I don't want to design a REST API (I've done it before, and wouldn't mind doing it again, but that's not the point); I want to design a library that makes it much easier to design one, by taking most of the work off of the coders and placing it on the computer. That's why I want a simple language, so I can focus on the library of tools.



  • @ScholRLEA said:

    I want to make them simpler, and more robust, which means (among other things) reducing a lot of the boilerplate that dominates most code in the majority of current languages.

    Honest question: do you believe C#/.net has a lot of boilerplate?



  • Actually, yes, compared to, say, Python, yes it does. Even Python and Ruby have a fair amount, but less than C#.

    And that's the thing: in Lisp it is a lot easier to roll up the required code into a box and make it work automatically than in most other languages. Things you can't not have in other languages are easily abstracted away. And despite what many people think, Lisp is multi-paradigm; it's just that functional programming is particularly easy in it. Admittedly, the current versions have problems with that: Common Lisp's CLOS is a bit too arcane for people trained in other variants of OOP, and Scheme doesn't have a native OO library (there are several out there, but there isn't a standard one). Clojure's is basically the same as Java's with all the boilerplate locked in. One of my goals in Thelema Language is to make sure that there is decent support for several ways of programming, not just functional.



  • @ScholRLEA said:

    Actually, yes, compared to, say, Python, yes it does.

    Wow. I've used Python and I could not disagree more.



  • I suspect that the disagreement here is over what 'boilerplate' refers to. When I am talking of boilerplate, I mean the code overhead that is baked into the language, things that have to be done to perform basic tasks which cannot be changed. All of the ENVIRONMENT section of a COBOL program is boilerplate. Import statements can be boilerplate, if there is no way to abstract them out. Most core syntax is boilerplate, because you usually can't replace it with something specific to a particular task.

    A simple Python program can in many cases run without a single import statement. I cannot imagine any way to do that in C# or Java, not even for "Hello World!". Not that import is a bad thing, but it is often a necessary thing, when it shouldn't always be so.


  • Discourse touched me in a no-no place

    @blakeyrat said:

    "here's a complicated recursive math problem expressed in 27 characters!!!"

    I'm sure you could do it in less if you used J. (Don't worry about looking it up; you will hate it. Heck, I hate it too. Unless you're a fan of APL…)



  • @dkf said:

    ScholRLEA said:
    I thought Modula-2 was a big thing

    I'm one of the very few people anywhere to have actually used Modula-3. I don't encourage a revival.


    I can't say I know much about Modula-3. My understanding is that it was an alternative to Oberon (Wirth's official semi-object-oriented Modula-2 successor) someone invented because they felt (understandably) that Wirth didn't understand and didn't really like OOP. If it was that badly implemented, no wonder I've hardly heard of it.

    As for Modula-2, which did get a fair amount of attention in the mid-1980s, it was basically Standard Pascal with the training wheels taken off and a module system hooked on. It was most decidedly not OOP in the modern sense, focusing more on what were at the time called Abstract Data Types. Wirth designed it mainly to overcome the limits of Pascal as a systems language. If it was like anything else, it would be the original Ada (which it had a lot in common with regarding it's design goals), but even Wirth wasn't as verbose as the DoD.

    Today, I wouldn't think much of the language, but at the time it seemed like a cleaner answer to C. In retrospect, it was the 'clean' (read: verbose and restrictive) aspects that did it in. I never had the chance to experiment with it, as the least expensive MS-DOS compiler for it, Whitesmiths', was about $300.



  • @blakeyrat said:

    I'm guessing he'd have to do about 47,000 of those things before he got to the point of "build a REST API".

    You guess wrong. Here's a (hacky) REST dispatcher written (and, of course, untested) in under 15 minutes. Another 2 minutes and you have an HTTP server implementing REST.

    The API is application-specific, of course, but there's no reason it should be any harder to deal with.

    (import (srfi :13) (srfi :14)
    
    (define (request-tokenize s)
      (let ([tokens (string-tokenize s)])
        (cond
         ;; Check the overall format looks good enough
         [(not (= 3 (length tokens)))
          (error 'request-tokenize "invalid request string" s)]
         ;; Check we have the right http type (not really good eough, but you see where I'm going)
         [(not (equal? "HTTP/1.1") (caddr tokens))
          (error 'request-tokenize "unknown request format" s)]
         ;; Check we have a valid method 
         [(not (member (car tokens) '("OPTIONS" "GET" "HEAD" "POST" "PUT" "DELETE" "TRACE" "CONNECT")))
          (error 'request-tokenize "unknown HTTP/1.1 method" s)]
         ;; OK, return list of tokens
         [else tokens])))
    
    ;; Here's your REST handler.  Takes a request string and 4 (optional) handlers for R, E, S and T
    ;; of the format (handler-function default-return) or #f
    ;; handler-function should be of the form (lambda (path request-lines)) and return a list consisting
    ;; of the return code and string
    ;; this should really strip out the headers and pass them separately with the "raw" body, but it's
    ;; just a horrible hack to prove a point.
    (define (rest-handler request . handlers)
      (let* ([(acceptable-chars (char-set-delete char-set:printing #\x10 #\x13))]
             [(request-lines (string-tokenize request acceptable-chars))]
             [(tokens (request-tokenize (car request-lines)))]
             [(handler (case (car tokens)
                         [("GET") car] [("PUT") cdr] [("POST") cddr] [("DELETE") cdddr] [else #f]))])
    
        ;; this is a horrible hack using conditionals for flow control.  But hey, TDWTF, amirite? 
        (or (and handler (handler handlers) (or ((car (handler handlers)) (cadr tokens) request-lines)
                                                (cdr (handler handlers))))
            '(500 "Error"))))
    

    … and yes, I did find another Discourse bug on posting this (can't wrap code blocks in small tags, but the preview window, as usual, lies to you about that).


  • Discourse touched me in a no-no place

    @ScholRLEA said:

    A simple Python program can in many cases run without a single import statement. I cannot imagine any way to do that in C# or Java.

    I can. I don't want to write programs that have such an impoverished outlook though.

    Look, it is good to use an existing library (package, module, whatever label you want to stick on it) to achieve the majority of a task. It saves you from having to fumble your way through the vast thicket of possible but inadvisable techniques to get to the point where you can do something in a wise way. All practical programming languages support this. Every. Last. One. The task is just too large to start from first principles each time. Having to state that you are using a library in some way might be perceived as boilerplate, but it is not really: it's really about avoiding a gigantic amount of work.

    Similarly, other things that you might think of as boilerplate might turn out to be not so much true boilerplate as support for working with much larger programs. Namespaces? Totally there for making it easier to write big programs. Classes/objects? Often a bit wordy, but definitely help people scale up in what they can cope with. Neither is strictly necessary — their semantics isn't really all that interesting in the end — but both make a huge difference in reality in helping a person understand what they are doing and what has been done. (Programming is not just for now, it's for the future too. Oh, and also to make things that machines execute, thought that's the least part of it.)



  • @dkf said:

    Look, it is good to use an existing library (package, module, whatever label you want to stick on it) to achieve the majority of a task.

    I quite agree; that's why I am extending the excessively limited import system created for R6RS in my language. The lack of a proper import system has been a major flaw in most Lisps for decades.

    The point wasn't that importing was a problem, the point was about being able to abstract away common detail. That, AFAIK, there isn't any easy way in C# to abstract the import statements out for a namespace as a whole, that is, make it so you only have to import a group of things once for the package as a whole and have them visible through the importing package, which I believe you can do in Python (to an extent). Every module has to import the same basic things over and over again, and that makes it boilerplate-ish.

    If a package - a nested group of related functions, objects, and classes - is frequently using certain other elements across the package, it makes sense that the imports should be shared at the package level. That's all I am saying about that. It's a repetition of redundant information that can and should be abstracted out, but I don't see that being the case in C#, or Java, or most other languages with import systems.



  • @dkf said:

    Similarly, other things that you might think of as boilerplate might turn out to be not so much true boilerplate as support for working with much larger programs. Namespaces? Totally there for making it easier to write big programs. Classes/objects? Often a bit wordy, but definitely help people scale up in what they can cope with. Neither is strictly necessary — their semantics isn't really all that interesting in the end — but both make a huge difference in reality in helping a person understand what they are doing and what has been done. (Programming is not just for now, it's for the future too. Oh, and also to make things that machines execute, thought that's the least part of it.)

    You're making assumptions on this, as I actually am adding a lot of those sorts of things, not omitting them. They will be library syntax rather than core language elements, but I do intend to have a standard object system[^]. Packages will be part of the base (auto-imported) library. I don't see them as boilerplate; I do, however, see the inability to abstract out the details of them as often resulting in boilerplate.

    (I've seen what happens when a Lisp dithers too long on the issue of object support; In Common Lisp, it resulted in a metaphorical camel, a system so obscure that most CLers simply avoid it, while in Scheme it led to a thicket of incompatible opposing object libraries. Even if I wanted to, I can't afford to avoid the issue, as anyone can roll an object system out of nothing more than closures, which would result in catastrophe of incompatibility for the language. I even told Paul Graham this when I heard he didn't want to have OO in his language at all, not that it mattered since Arc seems to be dead in the water.)

    EDIT: OK, where are the docs for Discourse Markdown? I can't figure out how to add a footnote on this fucking system.


  • Discourse touched me in a no-no place

    @ScholRLEA said:

    You're making assumptions on this

    No, just starting from a very different position. 😄

    • Will code be stating that it depends on interface or implementation?
    • Will you tackle the versioning problem?
    • How will you handle updating?
    • How will you handle allowing others to participate in building packages?

    You don't need answers to these questions immediately. You do need to think about them.
    @ScholRLEA said:

    (I've seen what happens when a Lisp dithers too long on the issue of object support; In CL, it resulted in a metaphorical camel, while in Scheme it led to a thicket of incompatible opposing object libraries. Even if I wanted to, I can't afford to avoid the issue, as anyone can roll an object system out of nothing more than closures, which would result in catastrophe of incompatibility for the language. I even told Paul Graham this, not that it matter since Arc is dead in the water.)

    Having actually written an object system (NB: not the class library, the object system itself) I know how hard it is to do, and it is not something that you take on lightly. Are you planning to make it really dynamic or just a little bit dynamic? (Don't say you're going down the classic C++ model. Just don't.) Will you be able to subclass the class of classes? (Doing so lets you avoid having new be anything magical.) Also, do you want to support anything more complex than single inheritance of implementation?

    How are you going to incorporate composition and delegation into your object system? There's lots of experience that suggests that inheritance is often the wrong way of sticking classes together, and is frequently over-used, but what do you plan to do to make alternatives easier to use?

    I can probably come up with more questions for you to think about, but these will do for now… ;)



  • I appreciate this; it's nicely constructive, and these are indeed questions I mean to address, and in some cases have.

    @dkf said:

    Will code be stating that it depends on interface or implementation?

    I'm not certain quite you mean by this; I have a few different possible understandings of this statement, and I am not sure which is the one you mean. If you mean, is the interface declared separately from implementation, as it is in, say, Ada, then no, I don't have that in mind right now, though it has occurred to me and I may reconsider it later. If you mean, do I have a system of declaring shared interfaces a la Java and C# interfaces or Common Lisp mix-ins, yes, I do. If you are asking if a given user can request a particular version of the object's implementation, then yes, the versioning should allow that. I still have the feeling you meant something else entirely, though.

    I do intend to handle versioning, at least on the module level, and have a simple model already in mind. As things stand, part of the import system would allow a range of versions to be set:

    (import (documents xml html5))
    (import (documents markdown discourse parser) :min-version (1 0 4 stable) :max-version ((1 9 5 stable))
    (import (controls conditionals) :min-version (0 0 1 alpha))
    

    I am still working on how the versioning will be set, as relying on the programmer to update something that should be automated is a Bad Thing. While I don't want to force the client-programmer to use a specific tool, it would be difficult to enforce versioning otherwise.

    However, I am concerned that it is much too simple and will be revisiting the matter in the future. It's a difficult issue and not one easily addressed.

    Updating is closely related to versioning, so any answer I give now is likely to change. As for collaboration, that's tricky; it would almost certainly require that a specific set of tools be used, if anything is to be enforced with the language. I need to give it more thought.

    I do mean for the object system to be dynamic; at the very least, method dispatch should take place at runtime, and I don't see any reason (even efficiency) why it should ever be bound earlier. The class of classes can indeed be subclassed, and while new will by default be a class method of Class, but it will always be possible to override it. Here is a simple example of a class in my current formulation:

    (def Animal      
       (Class ~new (Object)  
          :abstract #true
          (def species
             (property
                :domain String
                :mutable #false
                :visibility #public
                :default ""
                :initializer #generated
                :accessor #generated))         
          (def name
             (property
                :domain String
                :mutable #true
                :visibility #public
                :default ""
                :initializer #generated
                :accessor #generated
                :mutator #generated))
          (def speak
             (method ()
                :visibility #public
                :abstract #true))
          (ctor (s n)
                :visibility #public
                :init species s
                :init name n))))
    
    (def Dog
       (Class ~new (Animal)
          (def speak
             (method ()
                (system%console ^print "woof!")))
          (def new
                (ctor (name)
                   (this ~super "Canis familiaris" name))))
            
    (def toto (Dog ~new "Toto"))
    (toto ^speak)
    (toto ^get-name)
    

    This (and the other code examples) is just a rough sketch of my planned design; if nothing else, I'm probably going to cut down the verbosity a bit later on (e.g., replacing 'domain' and co-domain' with 'dom' and 'co-dom'). Still, it should give some idea of what I am after.

    @dkf said:

    How are you going to incorporate composition and delegation into your object system? There's lots of experience that suggests that inheritance is often the wrong way of sticking classes together, and is frequently over-used, but what do you plan to do to make alternatives easier to use?

    Hmmn, excellent point. It's a matter I am aware of, but I need to give it more thought, as I haven't specifically done anything to encourage composition or delegation over inheritance. I have been thinking of having a special treatment for what I refer to as 'roles', temporary class-like sets of properties and methods that can be added to or removed from an object as needed, but I'm not sure how that would affect the whole equation.


  • Discourse touched me in a no-no place

    @ScholRLEA said:

    I have a few different possible understandings of this statement, and I am not sure which is the one you mean.

    Sometimes, I mean several of the interpretations at once. As I said, these are questions that have to be thought about. They're also difficult to answer well, precisely because they're subtle.
    @ScholRLEA said:
    If you mean, is the interface declared separately from implementation, as it is in, say, Ada, then no, I don't have that in mind right now, though it has occurred to me and I may reconsider it later. If you mean, do I have a system of declaring shared interfaces a la Java and C# interfaces or Common Lisp mix-ins, yes, I do. If you are asking if a given user can request a particular version of the object's implementation, then yes, the versioning should allow that. I still have the feeling you meant something else entirely, though.

    The particular meaning I had principally in mind is that a package can be considered to be a collection of code and often auxiliary files as well (e.g., supporting textual configuration files, message catalogs, etc). There might also be some native libraries involved. The details don't matter.

    What matters though is that it is a whole entity, and that this identity is independent of exactly how the files that comprise it are described, and also that it is also independent of the location of the composite entity. If I need the http client package, I shouldn't have to say where it should be found; that should be something that the environment defines, not the code.

    Similarly, a package has a version. When you state the requirement for a package, you would typically state the requirement on the version of the package that provides the API you need. Now, you need to have some way of mapping that version into some way to find something that satisfies it, but again, that's not the code's responsibility. (Except for the code that implements the package management system.)
    @ScholRLEA said:

    (import (documents markdown discourse parser) :min-version (1 0 4 stable) :max-version ((1 9 5 stable))

    I really doubt that a :max-version will be usable in practice. Yes, theoretically some sort of interval logic ought to be perfect, but the complexity probably just isn't justifiable under the assumption that most package authors try to keep things working, and knowing what version to use as the last value of a range is ultra-difficult (typically such a value would only be assigned in the future; damn that causality!)

    The best approach is probably just to make the (exclusive) max version be the first one where the major version changes. That at least makes it pretty easy to explain to other programmers why something broke. ;-)
    @ScholRLEA said:

    However, I am concerned that it is much too simple and will be revisiting the matter in the future. It's a difficult issue and not one easily addressed.

    As noted above, it might actually be too complicated.
    @ScholRLEA said:
    I do mean for the object system to be dynamic; at the very least, method dispatch should take place at runtime, and I don't see any reason (even efficiency) why it should ever be bound earlier. The class of classes can indeed be subclassed, and while new will by default be a class method of Class, but it will always be possible to override it. Here is a simple example of a class in my current formulation:

    [Argh! Why is quoting code so blasted broken?!]

    Getting speed out of runtime dispatch requires some sort of caching mechanism. Knowing when to flush the cache and recompute is vital.

    Re your properties, you should consider making more of those things be defaulted. The more you can use sensible defaults so that things “do the right thing implicitly” the more resilient you can make user code against the future; new features that you slip in disrupt things less.



  • @dkf said:

    What matters though is that it is a whole entity, and that this identity is independent of exactly how the files that comprise it are described, and also that it is also independent of the location of the composite entity. If I need the http client package, I shouldn't have to say where it should be found; that should be something that the environment defines, not the code.

    Ah, I think I see now. In other words, the locations of the packages actual implementation should not of concern to the client-programmer; the 'paths' of the packages should be to the logical relationships between the packages, not to some specific file path. Having seen that sort of problem arise in both Java and Python, I tend to agree with you, though solving it makes for a complicated environment setup.

    @dkf said:

    I really doubt that a :max-version will be usable in practice. Yes, theoretically some sort of interval logic ought to be perfect, but the complexity probably just isn't justifiable under the assumption that most package authors try to keep things working, and knowing what version to use as the last value of a range is ultra-difficult (typically such a value would only be assigned in the future; damn that causality!)

    I was concerned about it, myself, but it seemed a logical step. Note that it is optional; unfortunately, it is really only useful if you are writing code after a known change, but if that's the case, why isn't the code being written for the new API? The problem (as you point out) is that, while one can expect that a future version may break the existing code, it is impossible to predict where such a break is going to occur.

    @dkf said:

    Getting speed out of runtime dispatch requires some sort of caching mechanism. Knowing when to flush the cache and recompute is vital.

    While I am quite some ways away from that point, I appreciate you bringing it up, as I will have to assay the issue eventually.

    @dkf said:

    Re your properties, you should consider making more of those things be defaulted. The more you can use sensible defaults so that things “do the right thing implicitly” the more resilient you can make user code against the future; new features that you slip in disrupt things less.

    That too is sensible; the problem is, which do you assume? In most languages, the assumption that a given property would be variable would be a no-brainer, yet in Lisp, it may make more sense to have the default be constant (as it is in R6RS Scheme's record types). If it is mutable, do you assume that the getters and setters are generated automatically, and only require that they be explicitly added if something particular needs to be done? I'm going to have to give quite a bit of thought to all these issues.



  • @ScholRLEA said:

    While I am quite some ways away from that point, I appreciate you bringing it up, as I will have to assay the issue eventually.

    I remember reading some rather nice analysis of when to cache pointers to selectors in Objective-C some years back (at least as far back as OpenStep, i.e. predating OSX). Googling "imp cache objc" should elucidate.

    Initially, I really wouldn't worry about runtime dispatch, though. Just do it dynamically.


  • BINNED

    As an example of an object system w/ MOP ( meta-object protocol ) take a look at Moose. Yes, it's Perl, but there is copious documentation on usage, implementation, and pointers to where the ideas came from.

    Truth time: OOP is one of Perl's "secret shames". There are inside-out class systems, hash based systems, classless class systems, closure based systems… ( It's nearly as bad as all the modules for config file or command line processing. )


  • Java Dev

    @ScholRLEA said:

    That too is sensible; the problem is, which do you assume?

    Well, in your code example above some properties seem redundant. For example, :default probably makes no sense without :initializer; so if a default is set but no initializer you could generate one.
    Similarly between :mutable and :visibility you may already know whether accessors or mutators should be generated.

    In the same vein, if your default is :mutable #false, then just specifying :mutable may be sufficient to set it to true. Just specifying :accessor without a value may imply generating one. Then again you may not want to do it if it reduces clarity in syntax elsewhere.



  • Adding special case syntax "here" always reduces clarity in syntax elsewhere.


  • Java Dev

    @tufty said:

    Adding special case syntax "here" always reduces clarity in syntax elsewhere.

    Never say never, but pretty much. At minimum it increases the size of the syntax (properties do not always have a value). I've also worked with syntaxes in the past where it was possible for a property without value to mean something different than that property being absent entirely, but this would be confusing in practice. Yet it can be visually pleasing particularly for boolean properties.


  • Discourse touched me in a no-no place

    @tufty said:

    Initially, I really wouldn't worry about runtime dispatch, though. Just do it dynamically.

    The cost of runtime dispatch — provided you've cached the building of the chain of method implementations that you dispatch along — isn't a big problem. The only case where the chain building is guaranteed to be cheap is where you've got single inheritance (of implementation; interface inheritance isn't as big a problem). If you're going to support things like multiple inheritance or some of the more sophisticated things that OOPLs have come up with over the years (i.e., post-C++) then the linearization algorithm is critical. And quite complicated once you get away from the most trivial beaten path, which is why a good cache is important.

    But only once it is working. Don't cache anything until you can cache the right thing.


  • ♿ (Parody)

    @ScholRLEA said:

    The point wasn't that importing was a problem, the point was about being able to abstract away common detail. That, AFAIK, there isn't any easy way in C# to abstract the import statements out for a namespace as a whole, that is, make it so you only have to import a group of things once for the package as a whole and have them visible through the importing package, which I believe you can do in Python (to an extent). Every module has to import the same basic things over and over again, and that makes it boilerplate-ish.

    I don't know python well, but this sounds terrible to me. You're hiding what's going on. I guess I'm disagreeing that this is boilerplate.


  • Discourse touched me in a no-no place

    @M_Adams said:

    Truth time: OOP is one of Perl's "secret shames". There are inside-out class systems, hash based systems, classless class systems, closure based systems… ( It's nearly as bad as all the modules for config file or command line processing. )

    Truth time: Tcl's as bad, except I'm clearing that swamp by putting in something more powerful, more dynamic, better engineered for robustness and faster than the alternatives. It's the only way to do it: make the thing that you're trying to displace the rest with much better in order to (slowly!) overcome the inertia of incumbency.

    The really hard bit is making sure that everything tidies up perfectly when you yank the rug out from underneath its feet…


  • Discourse touched me in a no-no place

    @ScholRLEA said:

    That too is sensible; the problem is, which do you assume?

    I can't tell you that: I don't have great instincts for what to assume with LISP variants. I suppose the right thing to do would be to make a mock-up (can be slow and ugly under the covers) and to try to use it for various common tasks. See if you end up having to state some patterns of things over and over. If something is really common, it perhaps ought to be a default, or maybe ought to have some sort of special form.

    The goal is to make the normal case easy and the tricky case possible (preferably without getting too tricky, but that's lower priority).


  • BINNED

    @dkf said:

    make the thing that you're trying to displace the rest with much better

    This.

    And that's basically what Moose is trying to do for Perl's OOP ecosystem. They're trying to hit some of the other warts, like parameter validation etc…

    I've not used Moose extensively ( just here and there ) due to speed issues ( which they are actively working on, but hey run time is slow time ) and I'll generally use an inside-out to keep people from messing about with my objects' innards.

    @dkf said:

    The really hard bit is making sure that everything tidies up perfectly when you yank the rug out from underneath its feet…

    That's even more difficult, and can result in a large amount of busted china before that skill is mastered ( sic ).



  • @ScholRLEA said:

    I suspect that the disagreement here is over what 'boilerplate' refers to. When I am talking of boilerplate, I mean the code overhead that is baked into the language, things that have to be done to perform basic tasks which cannot be changed. All of the ENVIRONMENT section of a COBOL program is boilerplate. Import statements can be boilerplate, if there is no way to abstract them out. Most core syntax is boilerplate, because you usually can't replace it with something specific to a particular task.

    A simple Python program can in many cases run without a single import statement. I cannot imagine any way to do that in C# or Java, not even for "Hello World!". Not that import is a bad thing, but it is often a necessary thing, when it shouldn't always be so.

    The import statement in Java and using directive in C# are different than import statements in Python.

    In Java/C#, all they do is allow you to use class names without fully qualifying them.

    To actually load the libraries in question, Java uses the classpath and .NET uses the References system.


  • Discourse touched me in a no-no place

    @M_Adams said:

    And that's basically what Moose is trying to do for Perl's OOP ecosystem. They're trying to hit some of the other warts, like parameter validation etc…

    I've not used Moose extensively ( just here and there ) due to speed issues ( which they are actively working on, but hey run time is slow time ) and I'll generally use an inside-out to keep people from messing about with my objects' innards.


    I completely understand, given that I've written the equivalent.

    Speed needs good caching and careful vetting of the call route to minimize the number of expensive operations required. For example, there should be ideally zero memory allocations (or disposals) on the critical path if the method is cached.

    Which is obvious. Except not always as obvious as all that in practice…
    @M_Adams said:

    That's even more difficult, and can result in a large amount of busted china before that skill is mastered ( sic ).

    I know that very intimately…



  • I'm making a new Dwarf Fortress video (I know you're all excited) and I realized that I should probably edit it. So I open Microsoft™ Windows™ Live™ Movie™ Maker™, load my video clips, add some music, and then set up custom export settings for the 800x300 UI of Dwarf Fortress. I also speed the video up by 4x for good measure.

    One hour of rendering later, I get back a faded video that has the 800x300 source video letterboxed in a 800x600 frame, then resized to fit the 800x300 output. 75% of my video is now a black frame.

    That one hour doesn't include the amount of time it took me to drag the videos into the right order (for some reason it put the first one last and the rest of them in order, so I had to drag the first clip past 5 hours worth of raw footage, and the Home key didn't work while I was dragging).

    Then I remembered that I had ffmpeg. Here is the entirety of what I needed to write to use ffmpeg to encode the video correctly:

    stream.txt

    file 'stream.mp4'
    file 'stream (01).mp4'
    file 'stream (02).mp4'
    file 'stream (03).mp4'
    

    audio.txt

    file '../Music/Dota 2/ti4/ui_startup_01.mp3'
    file '../Music/Dota 2/ti4/laning_01_layer_01.mp3'
    file '../Music/Dota 2/ti4/laning_01_layer_02.mp3'
    file '../Music/Dota 2/ti4/laning_01_layer_03.mp3'
    file '../Music/Dota 2/ti4/laning_02_layer_01.mp3'
    file '../Music/Dota 2/ti4/laning_02_layer_02.mp3'
    file '../Music/Dota 2/ti4/laning_02_layer_03.mp3'
    file '../Music/Dota 2/ti4/laning_03_layer_01.mp3'
    file '../Music/Dota 2/ti4/laning_03_layer_02.mp3'
    file '../Music/Dota 2/ti4/laning_03_layer_03.mp3'
    file '../Music/Dota 2/ti4/battle_01_1.mp3'
    file '../Music/Dota 2/ti4/battle_01_2.mp3'
    file '../Music/Dota 2/ti4/battle_02_1.mp3'
    file '../Music/Dota 2/ti4/battle_02_2.mp3'
    file '../Music/Dota 2/ti4/battle_03_1.mp3'
    file '../Music/Dota 2/ti4/battle_03_2.mp3'
    

    the motherbelgiuming command

    ffmpeg.exe -f concat -i stream.txt -f concat -i audio.txt -b:v 10000k -filter:v "setpts=PTS/4" -map 0:1 -map 1:0 -shortest stream_all.webm
    

    For those who don't understand ffmpeg, this says "take the files listed in stream.txt and audio.txt and encode them as 10Mbps video at 4 times the original speed with the video from the first file and the audio from the second file, stopping with whichever stream ends first, to stream_all.webm". I figured all that out by Googling what I wanted to do and doing what the tutorials said I should do, exactly as I did for the other program.

    And it actually works the way I told it to, unlike the other program's GUI.


  • BINNED

    @dkf said:

    Speed needs good caching and careful vetting of the call route…

    That's very true, and the very last bit of work ( and the hardest ); being an accounting geek, I don't care how fast you can get me the wrong answer. I'll wait ( not forever, but for quite a while ), if that's what it takes, for the correct answer. I get all twitchy for my nail gun around the "never time to do it right, always time to do it over" types.

    @dkf said:

    I know that very intimately…

    Yep, not everyone can become an amateur magician ( or be allowed to after so much broken china 😭 )… Dreams are so fragile.


Log in to reply