[HN Gopher] An Object-Oriented Language for the '20s
       ___________________________________________________________________
        
       An Object-Oriented Language for the '20s
        
       Author : ar-nelson
       Score  : 127 points
       Date   : 2021-03-13 14:13 UTC (8 hours ago)
        
 (HTM) web link (adam.nels.onl)
 (TXT) w3m dump (adam.nels.onl)
        
       | ajani wrote:
       | Most of what he mentions as features needed in this new language
       | have existed in common lisp for a long time. CLOS.
        
       | vips7L wrote:
       | Personally I'd keep exceptions. Result<T> is boiler plate hell.
        
         | dfgdghdf wrote:
         | Not if you have language support for monads / do-notation /
         | computation expressions.
         | 
         | Many "problems" that people have with functional programming
         | occur because they are trying to use FP techniques in a
         | language that isn't really equipped for functional programming
         | (JavaScript, C#, Java, Go...).
        
         | PaulKeeble wrote:
         | Having used Go a lot recently the err!=nil boilerplate is only
         | marginally worse than the monad approach which ends up with a
         | lot of match statements. Where monads work better is chaining
         | calls together, but at the cost of the of choice how to handle
         | the errors.
         | 
         | Exceptions end up being an elegant way to represent this, you
         | can choose how much you handle and where and match against the
         | types of errors. Everything else I have tried has been worse,
         | forcing me to handle errors I don't care about at that point in
         | the code with a boilerplate response to send it upwards,
         | something exceptions do automatically. I don't get more robust
         | code with explicit error handling I get more verbose code for
         | errors I can't do anything about. In practice it also changes
         | the interface of my methods far more with maintenance and
         | propagates error handling code throughout.
        
           | mpweiher wrote:
           | Yeah, the problem is call/return: you have to return
           | _something_. If you have nothing to return (or not yet -
           | async), things get tricky.
           | 
           | What I found really, really nice for error handling is
           | dataflow-style programming, because the main flow only has to
           | deal with the happy path. If you don't have a result, you
           | simply don't send anything to the next filter in the
           | pipeline.
           | 
           | And you report errors using a stderr style mechanism, so
           | error handling can be centralised and at a high-level, where
           | you actually know what to do with the errors.
        
           | jstimpfle wrote:
           | The key is to collect the errors in the subsystem where they
           | originated and to handle them at an appropriate time.
           | 
           | Try not to bubble up errors. Many subsystem interactions can
           | be unidirectional. If an error occurs, it is stored right
           | there. The caller might not even care. The situation can be
           | handled later.
        
             | TeMPOraL wrote:
             | Subsystem is too big of a boundary. GP and GGP are likely
             | talking about function level - which is where I also
             | experience this.
             | 
             | Imagine a situation as simple as:                 foo =
             | ComputeSomething()
             | 
             | Where ComputeSomething() is a complex operation, spread
             | into multiple functions, with many points that can fail for
             | various reasons. If a function four layers down the stack
             | from ComputeSomething() reports an error, this means all
             | three functions above it need to also pass it back as
             | Result<T>, and if you try to avoid the if-else hell, it
             | means the entire call graph under ComputeSomething() will
             | need to be wired with a monadic interface that smartly
             | doesn't execute functions when Result<T> is of Error type.
             | All it does is make you zoom through the call graph doing
             | nothing, just to simulate the default behavior of an
             | exception bubbling up the call stack.
        
             | ivan_gammel wrote:
             | Appropriate time is quite often at the end of the unit of
             | work. Let's say, your microservice is doing some
             | interactions with database and querying another services.
             | If an unlikely I/O error happens, DB will take care of the
             | uncommitted transaction and your service is just fine with
             | returning HTTP 5xx. Probability of this happening and
             | impact of not implementing error handlers all over your
             | stack are very low. Since the majority of developers are
             | not writing perfect programs, they will not spend the
             | money/time to avoid this risk. Exceptions may not help
             | writing a better program, but they are practical enough to
             | be supported in the language.
        
               | jstimpfle wrote:
               | This absolutely works for a HTTP request handler, where
               | all the error handling is implemented in the DB / garbage
               | collector, and where we're dealing with almost perfectly
               | isolated processes.
               | 
               | In more complex systems, this won't work out. Imagine a
               | software that controls sensors and motors, for example.
               | What if one of these devices fails? There is no way any
               | automatic system (like exceptions) could come up with the
               | right way to handle this situation.
        
               | ivan_gammel wrote:
               | There's no silver bullet that would solve error handling
               | in such cases purely by the means of the programming
               | language. Just like it's possible to have memory leaks in
               | Java, it is possible to write broken code with
               | Either<Result, Error> - imagine accidentally ignoring an
               | error from sensor that results in overheating and fire.
               | If you want to absolutely make sure, that this won't
               | happen, then you build your system in a way, where
               | prevention of the exceptional situations is implied by
               | design, rather than requires an additional effort from
               | developer.
        
               | jstimpfle wrote:
               | Yup. To continue with this example, an approach that
               | often works is to simply not have any error. For sensor
               | readouts, request new samples to be reported
               | asynchronously. (Coincidentally this is better for
               | performance as well).
               | 
               | In this way, the client code is basically required to
               | think -- oh, wait, what happens if I'm not getting
               | updates for some time beyond what is acceptable for me?
               | Also, in this way, the situation can be handled in a
               | single central location.
        
         | jstimpfle wrote:
         | Exceptions are great when you want errors to terminate a
         | (sub)program with an error message without having to type any
         | code at all.
         | 
         | That's suitable for batch programs and other short running
         | things where you can just scrap the incomplete computation and
         | re-try with a blank slate. I'm sure that can be productive for
         | webapp code that sits between a database and the frontend.
         | 
         | For longer running processes and more complex systems,
         | exceptions are not so great. It's almost a given that you will
         | have a hard time tracking down all the error scenarios in your
         | system.
        
           | wvenable wrote:
           | The last desktop app I wrote had a single error handler at
           | the event loop that displayed an error message and continued.
           | 
           | Because the stack unwinding cleanup is the same when an
           | exception occurs or when an operation completes normally this
           | app could recover from anything.
           | 
           | > It's almost a given that you will have a hard time tracking
           | down all the error scenarios in your system.
           | 
           | You don't need to track down all the error scenarios. The
           | only thing you need to worry about is things that you can
           | recover from to retry and things you can't.
           | 
           | Passing errors around places too much emphasis on where
           | errors occur. For recovery you only need to know what you can
           | recover from and where you can do that recovery. This is
           | usually no where near where the error occurs.
        
             | jstimpfle wrote:
             | > Passing errors around places too much emphasis on where
             | errors occur. For recovery you only need to know what you
             | can recover from and where you can do that recovery. This
             | is usually no where near where the error occurs.
             | 
             | This is actually the best argument why exceptions do not
             | "scale": Because in larger systems, the place where you
             | handle the error is almost certainly not up the call stack,
             | but in a different subsystem.
             | 
             | > Passing errors around places too much emphasis on where
             | errors occur. For recovery you only need to know what you
             | can recover from and where you can do that recovery. This
             | is usually no where near where the error occurs.
             | 
             | In a way, errors are just data, like everything else. There
             | is not much sense in making them something special - as I
             | said, with the exception of smaller script-like programs,
             | where you usually want to jump out of a larger subprogram
             | immediately, and rely on the garbage collector / stack
             | unwinding to clean up (hopefully, everything) for you.
        
               | wvenable wrote:
               | > There is not much sense in making them something
               | special
               | 
               | The reason you treat them as special is because at the
               | point an error/exception occurs you are saying that your
               | function (or program) can no longer do any more
               | meaningful work, forward progress is now impossible, and
               | you can't return anything meaningful.
               | 
               | We've gone backwards in making error returns explicit
               | because the most common and intelligent thing to do is
               | pass the error back up the chain or right out of the
               | subsystem entirely. The lazy approach is the correct one
               | with exceptions.
               | 
               | > Because in larger systems, the place where you handle
               | the error is almost certainly not up the call stack, but
               | in a different subsystem.
               | 
               | If your subsystem is network connected then the most
               | likely cause of an re-startable operation is a minor
               | network issue. There's no need to inform another
               | subsystem immediately. I don't really see how it's an
               | argument that exceptions don't scale. If you need to
               | inform another subsystem you can do that.
        
           | vips7L wrote:
           | > For longer running processes and more complex systems,
           | exceptions are not so great. It's almost a given that you
           | will have a hard time tracking down all the error scenarios
           | in your system.
           | 
           | I disagree. I absolutely love throwing exceptions from lower
           | levels of my web apps and letting them bubble to be handled
           | by exception mappers that will formulate the correct http
           | response from them.
        
             | chubot wrote:
             | parent: _I 'm sure that can be productive for webapp code
             | that sits between a database and the frontend._
        
             | ufo wrote:
             | I think the parent poster would classify that kind of web
             | app under as a "short running thing". Sure, the application
             | process might live for a long time but the requests are
             | short-lived and independent of each other.
        
               | TeMPOraL wrote:
               | Every program is a collection of a lot of "short running
               | things". Exceptions work well with this. There's a piece
               | of code that calls some function and expects a result or
               | an error; there's a piece of code much deeper in the
               | stack that can fail. Everything in the middle doesn't
               | care about the failure, except to make sure it doesn't
               | get executed if the failure occurs. Without exception-
               | like mechanism, you _have_ to make the middle care about
               | possible errors, just to prevent code past failure point
               | from executing. Your calls like foo(a) get turned into
               | if(a != error) { foo(a); }, even if it 's hidden behind a
               | nice monadic syntax.
        
       | enriquto wrote:
       | why is OOP still a thing?
        
         | AnimalMuppet wrote:
         | Because it's useful.
        
           | enriquto wrote:
           | people use the strangest tools
        
       | toolslive wrote:
       | multiple inheritance.                 c++ : both sides can bring
       | state. has diamond issues.       Java : one side can bring state.
       | other just signatures. too restrictive.
       | 
       | What you want is: both sides should be able to bring behaviour,
       | but only side can bring state. (iirc, ruby does it like that... )
        
       | fouc wrote:
       | I'm fine with implementing another static Object-Oriented
       | language, but please learn from some of the best non-static
       | Object Oriented languages out there like Ruby and to a lesser
       | extent Erlang/Elixir.
        
         | pkulak wrote:
         | Erlang/Elixir are two of the least OO languages I can think of.
         | Point still taken though. Elixir is an absolute joy, and the
         | more new languages take examples from it, the better.
        
           | andrewflnr wrote:
           | On the contrary, at least if we talk about Alan Kay's vision
           | then Erlang/Elixir are as close as we have today. Hint: the
           | processes are objects.
        
             | zokier wrote:
             | > at least if we talk about Alan Kay's vision
             | 
             | Big if there. Most people do not mean Kays OO when they say
             | OO.
        
               | dragonwriter wrote:
               | No, most people think of some ill-defined idea shaped
               | largely by the concrete design of early versions of, and
               | practice in, C++ and Java, reflecting a set of
               | compromises between early OO visions (including, but not
               | limited to, Kay's), C's particular style of statically-
               | typed procedural programming, and various practices that
               | emerged to deal with the warts of those compromises.
        
               | klibertp wrote:
               | Which is why they should be educated, given a chance. No?
        
               | xbar wrote:
               | I read "eradicated." But sure, let's educate them first.
        
               | jghn wrote:
               | I agree w/ your sentiments here, but it is worth keeping
               | in mind that Simula predates Smalltalk and could be
               | viewed as both the first OO language and the progenitor
               | of what became mainstream OOP.
        
               | klibertp wrote:
               | You're right. I'm constantly surprised how much of the
               | history of programming I'm still missing, despite trying
               | to learn as much as I can about it. The feature set of
               | Algol blew my mind when I first learned about it, for
               | example - and now I see I need to take a closer look at
               | Simula, which was developed as a superset/extension of
               | Algol. Learning about that is fascinating and really puts
               | the "language wars" of today into perspective.
        
               | jghn wrote:
               | BTW I saw you mention class oriented vs object oriented
               | elsewhere and I do think that captures the difference
               | pretty well, thus why I agreed with your sentiments. The
               | actor model basically _is_ object oriented programming in
               | that context, whereas what most people mean winds up
               | being class oriented.
               | 
               | There's (at least) a third model IMO from CLOS & friends,
               | such as Dylan, S4, and I'd argue Haskell Typeclasses. Not
               | sure what I'd call it but it's the idea where you define
               | data classes, generic functions, and then provide
               | linkages between those data classes and the generic
               | functions to give you methods on the state
        
               | AnimalMuppet wrote:
               | Kay was a visionary, but not every vision of his was the
               | final word on reality. It's possible that the
               | "mainstream" version of OO is _better_ than Kay 's.
        
               | klibertp wrote:
               | Yes, though I believe knowing about him and his vision
               | can be helpful in understanding the evolution of OOP and
               | its current mainstream incarnations. Basically, "those
               | who don't know history are doomed to repeat it", "history
               | repeats itself as a farce", and all that... :)
        
               | weatherlight wrote:
               | It's possible that I'm a coconut.
        
               | astrange wrote:
               | The mainstream version of OO is actually quite bad
               | though. You can think of it as just being functions where
               | the first parameter is to the left of the function name.
               | It has a few more features than that of course, but all
               | of these increase complexity and therefore bugs, and it
               | doesn't have any features that reduce bugs.
               | 
               | An example would be non-fragile ABIs (ObjC has this, C++
               | doesn't) or typestate/built in state machines (so you
               | can't call methods if the object is in an invalid state
               | for them). There's encapsulation at least, it's a start.
        
               | zokier wrote:
               | It doesn't really matter if its better or worse. As a
               | term OO means what people understand it to mean. Human
               | languages (unlike programming ones) are descriptive, not
               | prescriptive.
        
               | dragonwriter wrote:
               | > As a term OO means what people understand it to mean.
               | 
               | I would suggest that if you asked people to define the
               | term, Kay's definition would likely be the single most
               | common (even after normalizing variations in wording).
               | 
               | It would still only be a plurality, sure, but the
               | remaining majority doesn't share a common understanding,
               | either.
        
               | inopinatus wrote:
               | It's _possible_ that I'm a teapot.
        
               | DonHopkins wrote:
               | It's possible that weatherlight is a teapot and
               | inopinatus is a coconut!
        
           | klibertp wrote:
           | Actually, Erlang and Elixir are one of the most OO languages
           | out there. The original definition of OO, by Alan Kay, was to
           | have a lot of miniature computers, with state and computation
           | ability, called objects, which communicate by sending
           | messages to each other. Yes, calling a method in Smalltalk or
           | Self is called a "message send". Now, take a look at Erlangs
           | processes - doesn't it all sound very similar?
           | 
           | Edited to add: Kay also frequently says he's a proponent of
           | object oriented, not class oriented programming. Classes are
           | just a means of organizing the code, objects and their
           | relationships is what matters. In that way, modules in Elixir
           | are not inferior to classes in single-inheritance OO
           | languages: they offer the same level of sharing and composing
           | code (via macros, defoverridable, and defdelegate). Add to it
           | protocols, which can extend arbitrary types (modules) and you
           | get quite nice OO toolbox to use in Elixir.
        
             | hyperpape wrote:
             | My understanding is that Alan Kay cannot reasonably claim
             | to have "the original definition of OO", only "a very good
             | early definition". Additionally, it seems like he's either
             | been a bit inconsistent or unclear about the essential
             | ideas at different times.
             | 
             | One source: https://hillelwayne.com/post/alan-kay/
        
               | smt1 wrote:
               | Simula(67) definitely was the original
               | concept/implementation of OO. Smalltalk, C++, Java, etc
               | all derived different aspects of it.
               | 
               | - Main abstraction are classes (of objects).
               | 
               | - Concept of self-initializing data/procedure objects
               | Internal ("concrete") view of an object vs an external
               | ("abstract")
               | 
               | - Differentiated between object instances and the class
               | 
               | - Class/subclass facility made it possible to define
               | generalized object classes, which could be specialized by
               | defining subclasses containing additional declared
               | properties
               | 
               | - Different subclasses could contain different virtual
               | procedure declarations
               | 
               | - Domain specific language dialects
        
               | astrange wrote:
               | Note Java is actually explicitly based on Objective-C,
               | not on Simula or C++. They just removed most of the
               | Smalltalk bits to make it faster and less scary-looking
               | to C++ programmers.
        
               | frankpf wrote:
               | Hey smt1, looks like your account is dead and has been
               | for a while.
        
               | weatherlight wrote:
               | it's clearly not dead.
        
               | klibertp wrote:
               | Actually, it might be. The comment was dead, I vouched
               | for it, so it appeared.
        
               | keithnz wrote:
               | Alan has commented a number of times on a number of
               | threads on HN on this....
               | 
               | you can find his various comments here:
               | https://news.ycombinator.com/threads?id=alankay
        
               | hyperpape wrote:
               | I've seen his account. I'm not sure I've read every
               | comment, however. Is there one in particular that you
               | think says something not covered in the two pieces I've
               | linked in this thread?
        
               | inopinatus wrote:
               | Alan Kay provided the first definition of OOP. That is
               | not the same as using the word "object" for the first
               | time.
               | 
               | As that article clearly shows, at least once you're past
               | its clickbait title, Kay acknowledges his influences.
               | That isn't a lack of clarity, it's an awareness that we
               | all build on the effort of our peers and predecessors,
               | that ideas do not spring forth fully formed from the
               | void.
               | 
               | This does not discredit anyone's work, far from it;
               | context is an aid to understanding.
        
               | hyperpape wrote:
               | I could probably have been more specific.
               | 
               | What I mean by lack of clarity is that Kay in 1998 thinks
               | that messaging is the key idea, and suggests that was
               | always the key idea. But none of the historical sources
               | I've seen suggest he was clearly and consistently saying
               | that in the 70s.
               | 
               | That's really a minor point, it does nothing to
               | invalidate Kay's contributions. You can design a system
               | that has value, and only later find the best way to talk
               | about its value. That happens constantly: people create
               | something and then end up saying "in hindsight, I got X
               | right and Y wrong" or "back then, I knew this worked, but
               | it was only later that I knew why".
               | 
               | It doesn't disprove the idea that focusing on messaging
               | gives you the best version of OOP. It doesn't even
               | disprove that "real" OOP is about messaging (though I
               | think that argument has a high bar to clear--most terms
               | are messy).
               | 
               | What it does disprove is that messaging has a right to be
               | the core of OOP because Alan Kay defined it that way back
               | in the 70s.
               | 
               | P.S. Maybe the title isn't ideal, but Hillel was
               | literally reacting to someone who said Alan Kay invented
               | objects: https://lobste.rs/s/8yohqt/alan_kay_oo_programmi
               | ng#c_5xo7on. Actually, that might be a better source than
               | what I linked to, because it marshals more evidence that
               | Kay was not consistently saying "messaging is what
               | matters" during the 70s. He may have believed it, he may
               | have said it sometimes, but he wasn't consistent about
               | it.
               | 
               | Again, that's not a criticism of Kay, except the super
               | mild one that his 90s/2000s memory of what he said 20
               | years earlier wasn't perfect. But I forget shit I said
               | last week, so I'm not gonna judge.
        
               | klibertp wrote:
               | Good read, thanks. I knew about Simula, but never
               | analysed it deep enough to know how much of an effect it
               | had on early Smalltalk.
               | 
               | Still, the definition Kay gave as late as in 1998
               | resonates with me the most:
               | 
               |  _OOP to me means only messaging, local retention and
               | protection and hiding of state-process, and extreme late-
               | binding of all things._
               | 
               | And this is almost exactly what Erlang implements: just
               | add "asynchronous" before "messaging" and it fits
               | perfectly.
        
             | bitwize wrote:
             | "Object oriented as Alan Kay intended" has been a strapline
             | of Erlang proponents for some time. I think even Joe
             | Armstrong said it once. And I seem to recall Kay speaking
             | approvingly of the quip, and of Erlang itself.
        
         | hedora wrote:
         | Also, C++ has solved many of the issues mentioned in the
         | article (especially post C++11).
         | 
         | It has not solved the "minimum set of primitives that make OO
         | tenable" problem.
        
           | Kranar wrote:
           | Which problems from the article are you referring to?
           | 
           | C++ is likely the worst OO language in common usage and C++
           | developers rightfully avoid using its native OO functionality
           | as much as possible. They will literally jump through hoops
           | and write tons of additional boilerplate code to avoid
           | exposing the language's OO functionality (doing what C++
           | developers call type-erasure).
           | 
           | C++11 didn't improve OO in C++, it gave programmers many
           | features to avoid having to use it altogether.
        
             | AnimalMuppet wrote:
             | I have literally never seen someone program C++ that way. I
             | mean, it's probably true that some people somewhere are
             | doing that. But saying "C++ developers rightfully avoid
             | using its OO functionality as much as possible" seems like
             | a complete distortion of the actual situation.
        
               | Kranar wrote:
               | Those some people would be the authors of the standard
               | library who use type erasure for std::function,
               | std::string_view, std::span, std::any, std::ranges,
               | std::fmt, the new class of pmr memory allocators use type
               | erasure, the upcoming coroutines are built on type
               | erasure.
               | 
               | Common C++ libraries like boost have an entire library
               | dedicated to type erasure:
               | 
               | https://www.boost.org/doc/libs/1_75_0/doc/html/boost_type
               | era...
               | 
               | Facebook's Folly library also provides type erasure
               | functionality:
               | 
               | https://github.com/facebook/folly/blob/master/folly/docs/
               | Pol...
               | 
               | Google's Abseil is full of type erasure:
               | 
               | https://abseil.io/
               | 
               | Here is Adobe's C++ library for type erasure:
               | 
               | https://stlab.adobe.com/group__poly__related.html
               | 
               | And here's a talk by Sean Parent about how Adobe uses
               | type erasure to emulate runtime polymorphism without
               | using C++'s native OO features:
               | 
               | https://www.youtube.com/watch?v=QGcVXgEVMJg
               | 
               | I suppose these are just some people somewhere...
        
           | AnimalMuppet wrote:
           | A Turing Machine is the minimum (probably) set of primitives
           | that make computation possible. That doesn't mean we want to
           | program that way. I'm not sure we want to do OO programming
           | in the minimum set of primitives that make OO possible,
           | either.
        
       | FpUser wrote:
       | >"Object-oriented programming is out of fashion now"
       | 
       | Start with this one. It is not out of fashion. Supported by many
       | languages. Also languages do not exist to be cool and admired.
       | They are just a tools that help build things. Concepts like OOP
       | or any other style are not universal. Good for doing some things,
       | not so good for doing other things. Hoping that some concept will
       | magically solve world's problems is very naive at least.
        
         | wvenable wrote:
         | Complaining about OOP is like complaining about vaccines. The
         | positive results of OOP are so ubiquitous that it's invisible
         | to people. Instead the few negative reactions get all the
         | focus.
        
           | jstimpfle wrote:
           | Depends on what you mean by OOP. If you mean the concept of
           | handles, messages, and fault isolation, then OOP is the right
           | way to structure many solutions.
           | 
           | If you're talking about classes, methods, inheritance, and so
           | on, then it's just a syntax equivalent of training wheels. If
           | you want to progress to biking downhill at full speed, most
           | of this needs to come off at some point since the structure
           | is not suitable for more complex situations. This kind of OOP
           | strongly encourages componentization at the smallest level,
           | much much more than is beneficial from an organization
           | standpoint (subsystems).
        
             | astrange wrote:
             | > This kind of OOP strongly encourages componentization at
             | the smallest level, much much more than is beneficial from
             | an organization standpoint (subsystems).
             | 
             | Yep, and also even though OOP was designed as a way to
             | model simulation systems, it isn't the best thing for
             | actual popular simulation systems (video games). They use
             | more flexible patterns like entity-component systems.
             | 
             | OOP also wants to always layout data as array-of-
             | structures, which can be less efficient than structure-of-
             | arrays. Data layout is the most important thing for
             | efficient programs, since memory access is so slow.
        
             | wvenable wrote:
             | > then it's just a syntax equivalent of training wheels.
             | 
             | I wonder if there is a divide between people who programmed
             | before OOP was commonplace and those first experience is
             | learning it in school.
             | 
             | For me the idea that OOP is not suitable for more complex
             | situations betrays it's very conception.
        
               | astrange wrote:
               | Examples of alternative models to imperative OOP that
               | have languages:
               | 
               | https://en.wikipedia.org/wiki/Entity_component_system
               | (game-style C++)
               | 
               | https://en.wikipedia.org/wiki/AoS_and_SoA (Jai)
               | 
               | https://en.wikipedia.org/wiki/Relational_model (SQL)
               | 
               | https://en.wikipedia.org/wiki/Logic_programming (Prolog
               | and Mercury)
               | 
               | The goto critique of OOP used to be http://steve-
               | yegge.blogspot.com/2006/03/execution-in-kingdom... (as
               | seen in the article) but I think OO languages have
               | managed to steal enough good ideas from functional ones
               | by now that it doesn't apply so much.
        
               | wvenable wrote:
               | OOP is several orders of magnitude more successful than
               | all these alternatives combined. As I said, it's so
               | successful that its success is invisible. All that is
               | ever talked about are the tiny percentage of exceptions
               | where OOP doesn't work well like, for example, where
               | entity-component might be more appropriate. Those tiny
               | exceptions get way more attention.
               | 
               | Being contrarian to the massive success of OOP is great
               | way to get upvotes but it doesn't reflect reality. The
               | success of OOP is so huge that it's boring.
        
             | FpUser wrote:
             | >"If you're talking about classes, methods, inheritance,
             | and so on, then it's just a syntax equivalent of training
             | wheels."
             | 
             | Everything above microcode is. Abstractions / paradigms
             | when applied adequately help to see the forest beyond the
             | trees.
        
               | jstimpfle wrote:
               | I've never thought about it this way. To me it's more
               | like, many different paradigms and cleverness _are_ the
               | forest and as well the trees.
               | 
               | When I see an object-oriented codebase that makes me jump
               | between 5 or more files to understand the simplest
               | codepaths (because inheritance), and that is not
               | browseable without a properly setup IDE and jump-to
               | support (because of heavy namespacing and non-telling
               | method names), it makes my shy away.
               | 
               | On the other hand for example, larger open source
               | projects written in C are often really easy to read.
               | Local ("coherent") code paths, fully qualified function
               | names in calls, no extra foo that gets executed
               | implicitly on data data declaration or cleanup. Nothing
               | hidden, every line just works to solve the actual
               | problem.
        
           | FpUser wrote:
           | Many programmers are in general very opinionated about their
           | tools. I have no idea why. I do not get all that excited
           | about ways of doing things. To me they are just tools.
        
       | dw-im-here wrote:
       | I think this is a well written post, but I'm incredibly puzzled
       | at eschewing scalas syntax for type parameters and instead having
       | parentheses do double duty. Other than freeing up brackets for
       | indexing what is gained from this? Other than that I actually
       | think this is a language that deserves to exist so the ideas can
       | be tried out in practice, if nothing else to influence the
       | current OO languages to be better.
        
       | raspasov wrote:
       | An Object-Oriented language for the '20s: don't do it.
        
       | seanalltogether wrote:
       | Am I wrong or is OP describing most of what swift currently
       | offers. Optionals, extension where syntax, Result<> monads,
       | exceptions collapsed to a single line with try? syntax, safe
       | casting with as?, etc...
        
         | brobdingnagians wrote:
         | Same with kotlin. It appears that most of the advances are
         | really really great and emerged in a wave in the '10s. We are a
         | decade ahead of the curve ;) I think doing away with checked
         | exceptions was a definite benefit in that direction too..
         | 
         | Having higher kinded types and type classes like Haskell would
         | be nice though. I think there are definitely things in the
         | article which could be even better, but we've definitely moved
         | that way, and the next generation of language designers will
         | probably have even more insight into how to elegantly combine
         | those features as well.
        
           | virtualwhys wrote:
           | > Having higher kinded types and type classes like Haskell
           | would be nice though.
           | 
           | Don't have to leave the JVM, already there in Scala. And
           | Kotlin Arrow may get merged into the compiler, so Kotlin may
           | have first class support for these language features one
           | day...
        
             | hedora wrote:
             | Scala uses type erasure to preserve compatibility with the
             | JVM. No thanks. I want the compiler to statically verify my
             | types, and then optimize based on that.
             | 
             | At some point, I realized Java code tends to have many more
             | runtime type errors (in particular, NullPointerExceptions
             | and ClassCastExceptions) than similar C++ code (segfaults).
             | Things like rust should be even better than C++, but that's
             | practical largely thanks to performance optimizations from
             | llvm. Other than Java compatibility, I don't see any
             | advantage to the JVM at this point.
        
           | pjmlp wrote:
           | Or OCaml, Eiffel, Sather,...
        
       | mikewarot wrote:
       | It seems to me that you could do almost everything you want
       | (except for the immutable thing) in Free Pascal.
       | 
       | Pascal doesn't have the kingdom of nouns problem. It supports
       | good old fashioned procedures and functions.
       | 
       | Pascal can deal with nulls in strings, because we always know how
       | long they are.
       | 
       | Or perhaps you meant null pointers. Pascal avoids unnecessary
       | pointers, but supports them in a sane manner when you need them.
       | 
       | Pascal can deal with multiple inheritance in a sane manner, by
       | composition. Objects can have other objects as properties, and it
       | just works... no weird restructuring of everything horror stories
       | I've heard about in C++, etc.
       | 
       | Free Pascal has generics there are libraries that let you do
       | dictionaries and lists and trees of your type <T>.
       | 
       | I disagree about exceptions... proper handling of exceptions is a
       | good thing. A function should always return the type you expect,
       | not some weird value you have to test for.
       | 
       | As far as "pattern matching" I assumed you were talking about
       | regex (which can be addressed in a library)... but you're talking
       | about RTTI, or "reflection" where you can get type information at
       | runtime, which is a thing in Pascal and many other languages.
       | 
       | I don't understand the obsession with immutable variables.
       | 
       | [Edit: I think I understand... in Pascal, parameters are passed
       | by value as the default, but _can_ be passed by reference, it
       | depends on how you declare the function or procedure. It has
       | nothing to do with variables that you can 't assign new values
       | to, if I'm correct]
       | 
       | Did I miss anything?
        
         | mmazing wrote:
         | But it's not a hot sexy * new * silver bullet for all of your
         | problems.
        
         | elteto wrote:
         | Have to disagree on exceptions. The return type is part of the
         | function signature, it's part of its contract. If a function
         | can fail I want to see it right there on the return type and be
         | forced to handle it on the spot.
         | 
         | Exceptions are an invisible, out-of-band mechanism that will
         | crash your program because you forgot yet another try...catch.
         | Sum types with pattern matching are a much better approach for
         | writing safe and robust code.
         | 
         | The "proper handling of exceptions" panacea I keep hearing
         | about sounds a lot like "OOP done right"... it's supposed to
         | exist, yet no one seems to know what it actually is.
        
           | layer8 wrote:
           | > If a function can fail I want to see it right there on the
           | return type and be forced to handle it on the spot.
           | 
           | Checked exceptions solve this problem. Checked exceptions vs.
           | result sum types aren't a black and white dichotomy, however.
           | They are rather two points on a design spectrum, where one
           | design axis is how you want to syntactically handle error
           | escalation up the call chain, another axis is how you want to
           | handle destructuring/restructuring of the results, etc.
           | 
           | Unchecked exceptions are still useful for notifying bugs
           | (e.g. precondition violations) instead of aborting the OS
           | process on failed assertions.
        
           | TeMPOraL wrote:
           | > _Exceptions are an invisible, out-of-band mechanism that
           | will crash your program because you forgot yet another
           | try...catch._
           | 
           | Out-of-band is the point. It lets you write your code for the
           | correct path, without having to explicitly string a bunch of
           | error types along (or do something annoying like mandating
           | all errors are of type Error, which is a string).
           | 
           | The problem is with invisibility, which is the case with most
           | exception-using languages. Java had it solved right, though,
           | with checked exceptions. Of course back in the 2000s we were
           | a bunch of lazy children and said "checked exceptions are bad
           | because they make us declare stuff up front", which was
           | actually _a good thing_ , but we couldn't understand that, so
           | they became optional in Java.
           | 
           | (On the other hand, exception handling is just a poor shard
           | of proper condition system, like in Common Lisp.)
        
             | elteto wrote:
             | If you squint hard enough checked exceptions are sum types
             | through other means:                 try         foo()
             | catch ExceptionX         ...handle X...       catch
             | ExceptionY         ...handle Y...       catch ExceptionZ
             | ...handle Z...
             | 
             | compare that to (borrowing some Rust-ish syntax):
             | match foo() {         ExceptionX => ...handle X...
             | ExceptionY => ...handle Y...         ExceptionZ =>
             | ...handle Z...       }
             | 
             | with the added benefits that sum types can be used outside
             | of error handling.
        
               | TeMPOraL wrote:
               | Yep, they are essentially sum types, but out-of-band,
               | which is important.
               | 
               | In particular, you can have:                 def quux():
               | xkcd = frob(foo(), zomg())         ...lots of code...
               | def bar():         try           quux()         catch
               | ExceptionX           ...handle X...            def baz():
               | try           bar()         catch ExceptionY
               | ...handle Y...
               | 
               | I assume pattern-matching can be partial without explicit
               | default value, but (in the languages I've dealt with) it
               | gets tricky with expressions. For instance, in C++:
               | auto a = foo();       auto b = bar(a, quux());
               | return frob(xkcd(a), b);
               | 
               | if foo(), bar() and quux() can throw exceptions, and you
               | wanted to replace them with sum types - say,
               | std::variant<F, Err1, Err2, Err3>, or for better semantic
               | separation (more on this below), tl::expected<F,
               | std::variant<Err1, Err2, Err3>> - you'll going to pray
               | you have enough "monadic" methods in those classes
               | (spoiler alert: you don't, as of C++17/current
               | tl::expected) to rescue the spaghetti code you'll have to
               | write to model this flow.
               | 
               | Recently, I've been dealing with a C++ codebase that uses
               | tl::expected in lieu of exceptions, and while the "main
               | flow" becomes more readable - return doThis(x,
               | y).and_then(do_that).map(sth).map_error(logErrors); - the
               | side effect is that the codebase is _littered_ with
               | trivial functions and closures to make that interface
               | work. It 's much less readable than a bunch of catch
               | blocks every now and then. Maybe it's a limitation of
               | C++? I feel like a syntax for partial application would
               | remove half of the boilerplate I have to write when
               | chaining tl::expected-returning functions.
               | 
               | On semantic separation, I alluded to using a "nested" sum
               | type like (Result1 + Result2 + ...) + (Err1 + Err2 + ...)
               | instead of Result1 + Result2 + ... + Err1 + Err2 + ... -
               | while mathematically equivalent, they aren't
               | syntactically equivalent in code. Grouping possible
               | return value types separately from possible error type
               | values improves reasoning about code - the groups tend to
               | change independently from function to function. And if
               | you squint, this is what exceptions give you: they
               | separate the error sum type from return sum type, and
               | give the former a completely different control path. This
               | means you can think about them separately, and don't have
               | to force-fit the success path into shape that satisfies
               | the needs of the error path.
        
             | astrange wrote:
             | Typed checked exceptions are a bad thing because they
             | require libraries to redeclare things that are its
             | implementation details, in particular the API of other
             | libraries they use internally.
             | 
             | See Swift's error handling design: https://github.com/apple
             | /swift/blob/main/docs/ErrorHandlingR...
             | 
             | (Also, exceptions/nonlocal returns are a pain because the
             | invisible control flow is hard to reason about and hard to
             | implement in a compiler.)
        
               | TeMPOraL wrote:
               | > _they require libraries to redeclare things that are
               | its implementation details_
               | 
               | Exceptions that can fly out of a library are just as much
               | a part of its public interface as the error types it uses
               | for return values. Adding a checked exception Exception
               | is equivalent to turning your Foo return value into
               | Result<Foo> - the code will no longer work if it doesn't
               | handle it.
               | 
               | > _in particular the API of other libraries they use
               | internally._
               | 
               | The library can make a choice between handling the
               | internal exceptions at the interface boundary, rewrapping
               | them, or exposing them as a part of its public interface.
               | That's no different than a library considering returning
               | a type that's defined in another library it uses
               | internally.
               | 
               | > _(Also, exceptions /nonlocal returns are a pain because
               | the invisible control flow is hard to reason about and
               | hard to implement in a compiler.)_
               | 
               | I'm not a compiler guy so I'm not going to dispute the
               | issues with implementing exceptions, but on the
               | readability flow, I strongly disagree. There's nothing
               | hard in exception-based control flow. The rule is simple:
               | a given function either succeeds, returning what it
               | declares, or fails - a failure cause the caller to enter
               | the appropriate exception handling block if it has one,
               | or exit immediately, propagating the exception up the
               | stack.
        
           | mikewarot wrote:
           | So how do you want to handle something like sqrt(-1.0) for
           | real numbers? A runtime exception seems the sane way to do it
           | for me. Are you suggesting that sqrt(-1.0) or any other
           | invalid value should return a sentinel value like -999, or
           | should return NUL instead of a real?
           | 
           | Any way of dealing with errors has to be out of band, we just
           | disagree on the mechanism.
        
             | qsort wrote:
             | sqrt(-1.0) is NaN, it's typically used in numerical code
             | where you can't stop and check for exceptions because
             | performance is key.
             | 
             | But just for the sake of argument, the type of 'sqrt'
             | isn't:                   sqrt : R -> R
             | 
             | it is:                   sqrt : R+ -> R+
             | 
             | So you have two options here. You can have sqrt return
             | "Either Error Double" and force the programmer to deal with
             | the possibility of an error (Rust does this) or you define
             | a "nonnegative real" type that the compiler can guarantee
             | will be an acceptable input for sqrt. The latter is much
             | harder to do.
             | 
             | But either way no, you only need an "out of band" mechanism
             | when the type system isn't strong enough or isn't being
             | properly leveraged.
        
               | mikewarot wrote:
               | I used square root because it is commonly known that
               | negative numbers are a problem for them. What about
               | tangent(x) which periodically blows up? A type system
               | can't fix that.
               | 
               | In my opinion it is far better to throw an exception that
               | can be explicitly handled, rather than cause some math
               | error at some random point later because NaN or some
               | infinity snuck by as a number.
        
               | qsort wrote:
               | Math.tan will never throw exceptions because, for the
               | exact same reason as above, it won't check its input.
               | 
               | But again for the sake of argument, the return type of
               | tan() could be "Either<Error, double>".
        
               | mikewarot wrote:
               | I guess I would get used to that, but it seems broken to
               | me. The way to deal with that is out of band, checking
               | for the type to change to "Error"
               | 
               | In Pascal tan(pi/2) or tan(3pi/2) causes an overflow. The
               | way to deal with that is out of band, to handle an
               | exception of type E_Overflow.
        
               | qsort wrote:
               | Again no, it isn't. You are well entitled to believe
               | Pascal is the best thing since sliced bread, but the two
               | ideas are fundamentally different.
               | 
               | There is no type change. The function "tan" would return
               | a value of type "Either<Error, double>". An expression
               | such as:                  double x = Math.tan(Math.PI /
               | 8)
               | 
               | would fail the compile-time type check, because the type
               | of the variable "x" is "double" and the type of the
               | expression "Math.tan(Math.PI / 8)" is "Either<Error,
               | double>".
               | 
               | It would be the responsibility of the caller to prove to
               | the type system that the error has been handled, with
               | either a built-in language facility a la ML, or with ad-
               | hoc code such as a Bohm-Berarducci construction.
               | 
               | Let's just agree to disagree.
        
               | mikewarot wrote:
               | I _know_ Pascal isn 't perfect, and lots has been learned
               | since then... this is genuine confusion on my part. So
               | tan in your system returns a variant type (could be
               | Error, could be Double), right? (I didn't get that)
               | 
               | Compilers can find all sorts of things they couldn't in
               | the past, which is amazing.
               | 
               | What happens at run-time if an overflow happens?
               | 
               | again... I'm sorry if I didn't express myself clearly
               | enough. I'm coming at this from a Pascal programmers
               | perspective... I've missed the past 20 years of compiler
               | improvements.
        
               | SAI_Peregrinus wrote:
               | In Rust in particular (and many other languages) tan
               | operates on IEEE 754 double-precision floating points
               | ("double" for Free Pascal, "f64" for Rust). These can
               | contain infinity and NAN values, not just numbers. So if
               | tan blows up, you get NAN back. It's your job as the
               | programmer to check that the result wasn't NAN (or INF)
               | before proceeding. "Double" is already such a variant
               | type. So you have to check your results which can return
               | an error variant for errors, just like you already do
               | with floating-point values.
        
               | setr wrote:
               | Rust chooses to panic, I believe, but you could always
               | have applied the same Error strategy -- it'd just be
               | really annoying. The stdlib also provides a bunch of
               | methods to pick-your-strategy for overflow behavior, but
               | you need to use them instead of the +/* operation
        
               | tene wrote:
               | Like you said, this is a variant type that represents
               | either a value, or an error. Here's the definition of
               | Rust's Result type:                 pub enum Result<T, E>
               | {           Ok(T),           Err(E),       }
               | 
               | I threw together some examples of how you can use a
               | Result value, although I used arcsin instead of tan as
               | the out-of-domain values are much easier to specify.
               | 
               | https://play.rust-
               | lang.org/?version=stable&mode=debug&editio...
               | 
               | I'm not entirely sure what you're asking about regarding
               | runtime behaviour, but here's two guesses.
               | 
               | As a user of a function that returns a Result type, at
               | runtime the function returns a result that has a flag and
               | a payload, where the flag specifies whether the payload
               | is your value or an error. The only ways to get a value
               | out of the result are either to check the flag and handle
               | both possibilities, or to check it and crash the program
               | if it's an error.
               | 
               | As someone writing a function that returns a result type,
               | you write whatever logic you need to determine whether
               | you've successfully produced a value, or which error
               | you've produced, and then you either
               | return(Ok(my_value)); or return(Err(my_error));. If
               | you're doing floating point math, this might be checking
               | an overflow flag, or looking at your inputs, or checking
               | for NaN after operating, or whatever makes sense for your
               | task.
               | 
               | Using a variant/product/discriminated union like this is
               | orthogonal to the details of floating point operations at
               | runtime. What it lets you do is have the compiler enforce
               | that users of your function must check for errors, as
               | it's a type error to assign a Result<f64,E> to a variable
               | of type f64.
               | 
               | I hope that helps! Do you have any more questions about
               | this, or things I didn't address, or topics where you'd
               | like more explanation or examples?
        
               | hedora wrote:
               | Sqrt(-1.0) should return NaN or raise a signal. Let the
               | programmer choose. Throwing an exception doesn't help
               | anyone.
        
           | hedora wrote:
           | There have been many studies of exception handling
           | correctness. They've shown that no one can correctly write
           | exception handlers.
           | 
           | In my experience, proper exception handling is more difficult
           | than advanced topics like lock free data structures, with the
           | disadvantage that junior programmers have been taught to use
           | exceptions and avoid lock free primitives.
        
         | qsort wrote:
         | I'm not the author, but:
         | 
         | > Or perhaps you meant null pointers.
         | 
         | A major problem in languages like Java is that objects can
         | always be null. A better solution would be to force strict null
         | checks a la Typescript, where if you want to take or return an
         | optionally null reference, you have to explicitly encode that
         | property in the type of the variable.
         | 
         | NullPointerExceptions are frankly a really stupid thing we have
         | to deal with. A proper type system could check for that at
         | compile time.
         | 
         | > but you're talking about RTTI, or "reflection"
         | 
         | OP is talking about ML-style type destructuring, which is kind
         | of the opposite of this.
         | 
         | > I don't understand the obsession with immutable variables.
         | 
         | Mutating variables leads to all kinds of invisibile problems
         | and makes the code a lot harder to reason on. When I write
         | Java, most of my variables are final, the author is right that
         | we should be taking the opposite convention a la Rust,
         | variables should be immutable by default with mutability
         | explicitly annotated.
        
           | mikewarot wrote:
           | >Mutating variables leads to all kinds of invisibile problems
           | and makes the code a lot harder to reason on.
           | 
           | Am I correct in interpreting that as when you pass a variable
           | to a function _by reference_ it causes problems? Pascal
           | defaults to passing them by value.
        
             | qsort wrote:
             | Sure, but passing by value isn't always realistic. If you
             | have a hashtable with a million entries, you can't copy it
             | just to update a value.
             | 
             | There's also the problem of internal mutation of state,
             | with hashtables/collections being again primary examples.
             | It's not always avoidable to have internal mutation, but it
             | shouldn't be the default, in most cases it's just wrong.
        
               | mikewarot wrote:
               | That's a weird corner case... I wouldn't expect the
               | compiler to keep me from shooting myself in the foot like
               | that. I'm surprised that some people do expect it.
        
               | edjrage wrote:
               | Ever seen any of the top 5 functional languages?
               | 
               | And "weird corner case"? Really?
        
               | qsort wrote:
               | What do you honestly expect me to answer? That's kind of
               | a moot point: the entire reason we have type systems in
               | the first place is that they are partial, machine-
               | checkable proofs of correctness. The entire discussion is
               | how to strike the best power/practicality balance while
               | keeping the thing decidable. Keeping you from shooting
               | yourself in the foot is exactly the type system's job.
               | 
               | Why do you even bother with static types if you seriously
               | believe that?
        
               | mikewarot wrote:
               | I believe in type systems as a way of knowing how data is
               | encoded in memory. I don't expect them to somehow prevent
               | me from writing that memory location more than once.
        
               | badsectoracula wrote:
               | Have you ever used                  procedure
               | Foo(constref Something: SomeType);
               | 
               | instead of                  procedure Foo(var Something:
               | SomeType);
               | 
               | in Free Pascal? Both will pass the same stuff in memory
               | (stack), but one has different semantics.
               | 
               | Similarly                  const NiceFeature = $01;
               | NeatFeature = $02; GoodFeature = $04;        var
               | Features: Byte;
               | 
               | and                  {$packset 1}        type Feature =
               | (Nice, Neat, Good);        var Features: set of Feature;
               | 
               | would be stored the same way in memory but again,
               | different semantics with the compiler helping you in the
               | second case to avoid logic bugs (e.g. setting Features to
               | an invalid value).
               | 
               | These are cases where Free Pascal helps you from shooting
               | yourself in the foot.
        
               | mikewarot wrote:
               | I've never used ConstRef, nor $packset
               | 
               | I can see the value of making sure you don't end up with
               | invalid values.
        
               | astrange wrote:
               | > Sure, but passing by value isn't always realistic. If
               | you have a hashtable with a million entries, you can't
               | copy it just to update a value.
               | 
               | What else are you going to do? If you want to insert a
               | value but also have access to the hashtable without the
               | change, then you need to copy it. (Or use a data
               | structure with history tracking, like RCU or finger
               | trees.)
               | 
               | If you don't need the old value, then you can optimize
               | out the copy - this isn't too hard.
        
       | rini17 wrote:
       | It seems to me more a syntactic sugar than really tackling the
       | main problem with OO: state that tends to get scattered into many
       | interdependent objects, hard to manage, hard to refactor.
        
         | mikewarot wrote:
         | >state that tends to get scattered into many interdependent
         | objects, hard to manage, hard to refactor.
         | 
         | Care to elaborate what you mean by that?
        
       | snidane wrote:
       | I'll bite.
       | 
       | I believe the consensus for the 20s for error handling is to have
       | both exceptions and error types (or just error pairs). Without
       | exceptions the code starts to bloat up with various try's, ?s,
       | unwraps, pattern matches on sum and option types and nonstandard
       | do notations. You start writing a nice and simple code when
       | prototyping, only to end up with unreadable piece of mess when
       | productionizing and wrapping everything with error handling. Most
       | modern languages which boast for not using exceptions have them
       | anyway, but call them panics or aborts and don't provide first
       | class support for them.
       | 
       | I believe the next paradigm beyond OO and FP is data oriented
       | programming. One could argue that it is what FP is, but modern FP
       | means more Type Oriented programming than Functional Programming.
       | In contrast with Lisp and APL family, which are also considered
       | FP, but whole another class.
       | 
       | OO gets lots is the kingdom of nouns, as the article points out.
       | You often can't even use a stupid function without wrapping it in
       | some class. Type programming makes a type for everything and gets
       | lost in the kingdom of types. You often can't even start
       | programming before you type your data or provide schema for it.
       | 
       | In Data Oriented Programming, you try to limit the number of
       | objects or types to minimum, such as simple linked lists (Lisp)
       | or multidimensional vectors (APL, numpy, tensorflow), json dicts
       | (python, jq), streams of text (shell), tables (sql) or primitives
       | - strings, ints, etc. The big benefit is that you can start
       | coding immediately without defining any classes or types and most
       | operations are already defined for you in a performant way. Once
       | you introduce your custom classes or types, you lose the benefit
       | of carefully designed operations on the language primitives that
       | allow you to stay within the algebra of those language
       | primitives. Eg. in APL you have arrays and numbers and they
       | combine in infinite amount of ways which have been carefully
       | explored and designed over decades for very ergonomic use.
       | 
       | I believe the future is to have small amount of versatile types,
       | not to create languages which promote complexity by introducing
       | tons of non-interoperable custom types.
        
       | throwaway4good wrote:
       | "First, a few obvious, non-negotiable things any new OO language
       | should have: ..."
       | 
       | Well, that's like your opinion, man ...
        
         | throwaway4good wrote:
         | Personally I would drop the static type system and reinvent
         | Self.
        
           | agumonkey wrote:
           | time to dust it off and sell it as new ?
        
             | throwaway4good wrote:
             | Yeah - I think the kids are ready for that.
        
               | agumonkey wrote:
               | rebrand it `selfie`
        
           | protomyth wrote:
           | ...and steal the Soups from NewtonScript on the way.
        
             | mucholove wrote:
             | What are the soups?
             | 
             | Both Google and DuckDuckGo give me no results so an example
             | would be appreciated.
             | 
             | (Hope I'm not caught up in a prototype joke :)
        
               | [deleted]
        
               | lionsdan wrote:
               | "Data in Newton is stored in object-oriented databases
               | known as soups."
               | 
               | https://en.wikipedia.org/wiki/Apple_Newton#Data_storage
        
               | protomyth wrote:
               | Chapter 11-7 of https://www.newted.org/download/manuals/N
               | ewtonProgrammerGuid... using the Self influenced language
               | NewtonScript https://www.newted.org/download/manuals/Newt
               | onScriptProgramL...
        
               | astrange wrote:
               | My impression of systems like soups/OpenDoc/component
               | systems/etc is that they're doomed to failure because
               | they don't respect Conway's law. The app model works
               | because each app has its own development team and teams
               | aren't directly editing other teams' data.
               | 
               | They don't have the proper expertise and will break
               | something, because it's not possible to enforce data
               | consistency well enough. (Apple's modern OO database is
               | called CoreData, has a pedigree from WebObjects, and its
               | consistency features are pretty hard to use in practice.)
        
           | stitched2gethr wrote:
           | After working in Python and Ruby I don't think I could ever
           | choose a dynamically typed language for a project with more
           | than a few thousand lines. There's just a lot of safety, and
           | documentation, you get cheaply when using static types.
        
             | fiddlerwoaroof wrote:
             | There's languages like Python and Ruby and then there's
             | languages like Smalltalk, Common Lisp, Self or Clojure: if
             | dev-time is runtime, you miss compile-time checks a lot
             | less
        
             | weatherlight wrote:
             | Ruby has conventions and a big testing culture which helps
             | mitigate a lot of those issues. I also work with Typescript
             | and I see whole classes of bugs that could be avoided if
             | everyone just stuck with conventions and best practices.
             | 
             | If you like Ruby and wish it had types, check out Crystal.
             | It's basically Ruby with Go's concurrency model, and it's
             | as fast as Go.
             | 
             | {Edit: repetition}
        
         | _void wrote:
         | Lol, can't ignore the Lebowski reference :) good one! I
         | actually think it is very subjective list :)
        
         | brabel wrote:
         | That's the problem with language design: too much opinion, too
         | little decision making based on actual data.
         | 
         | The author says there should be no Exceptions and there should
         | be multiple inheritance... I mean, these seem like pretty
         | extreme positions to have when talking about an OO language...
         | if I remember correctly, multiple inheritance, in particular,
         | was one of the features the languages from the late 90's had
         | actually stayed clear of as it had been shown by the previous
         | generation of languages that it was a mostly bad idea. I think
         | even inheritance as done in Java is not the best of ideas, and
         | there seems to be evidence extension methods or, similarly,
         | type classes, are better suited to add behaviour to data
         | structures.
         | 
         | IMO languages like Kotlin and Swift are much more like what
         | modern OOP languages should be than what the author proposes.
        
           | renox wrote:
           | Bah MI was given a bad reputation by Java fans but their
           | arguments weren't very persuasive.. The diamond issue? As
           | shown in the article, it isn't really an issue, Eiffel
           | actually use this solution.
        
           | hedora wrote:
           | If you want your language to be called "Object Oriented", it
           | needs to support implementation inheritance. (Encapsulation
           | and interfaces predate OO).
           | 
           | My personal take is that implementation inheritance is a bad
           | idea, and, by extension, Object Oriented programming is a
           | dead end.
        
           | andrewflnr wrote:
           | The author is explicitly using Kotlin as one of their
           | inspirations, and IMO has a pretty reasonable approach to
           | multiple inheritance (and I suspect they would agree that
           | extension methods are preferred when applicable). What
           | problems does MI cause that aren't fixed by OP's solution to
           | diamond inheritance?
        
       | sriku wrote:
       | Erlang is perhaps the best OO lang out there that doesn't look
       | like one but it's principles are the heart of OO.
       | 
       | PS: I admit I lost the author at "scala is my favourite language"
       | and so am biased.
        
         | hderms wrote:
         | Scala can be one of the most productive, elegant, reasonably
         | performant languages to work in or it can be the opposite of
         | all those. When done right it's amazing but the language is
         | complex enough to permit some questionable choices
        
           | blackrock wrote:
           | Life is too short to be forever tied to Java. This will be
           | Scala's downfall.
           | 
           | Elixir looks like the more promising approach. I love its
           | pipe forwarding operator.
        
             | zokier wrote:
             | scala-native exists?
        
             | dw-im-here wrote:
             | Life is too short to reimplement libraries and have them
             | battle tested. As a scala dev I find this a very weak
             | argument
        
               | weatherlight wrote:
               | Except like the BEAM/erlangOTP(and by extension Elixir)
               | is older than the JVM, and arguably as battle tested, if
               | not more....
        
             | pjmlp wrote:
             | Life is to short to reinvent the JVM and .NET runtimes, and
             | IDEs, poorly on FOSS budgets.
        
       | alkonaut wrote:
       | I love how rust does interfaces (traits) and allows implementing
       | _outside_ the type. It feels much more natural to create a
       | subsystem on the side without tangling it into other subsystems.
       | E.g. to create a separate rendering subsystem for a game entity
       | you simply                   impl Drawable for MyEntity
       | void Draw(MyEntity self, Canvas...
       | 
       | Which feels quite natural compared to anemic-entity ECS or naive
       | Composition-over-inheritance like so:                   class
       | MyEntity {           EntityDrawingComponent drawer
       | EntityMovingComponent mover;
       | 
       | and of course, over the standard Java/C# OO way
       | class MyEntity : Drawable, Movable, ...           void
       | Move(Vec..)           void Draw(Canvas..)
       | 
       | With this type of declaration, I'm forced to mix the code for my
       | different subsystems making it not just likely but inevitable
       | that someone eventually ties these subsystems together.
       | 
       | In general, if I were to design a "better" OO language now; i'd
       | make sure to make it almost not OO at all. I'd allow subclass
       | polymorphism, but no virtual methods or classes. Only
       | interfaces/traits and abstract classes. I'd very clearly separate
       | data types from identity types at the language level (which
       | aren't just a difference betweeen stack and heap as with C#
       | structs and classes).
       | 
       | I'd want the bare minimum of functional niceness: enumerations
       | and pattern matching which check for exhaustion. Having that in a
       | lanugage easily lets the developer use different implementation
       | for two _completely_ different cases: open and closed
       | polymorphism sets. OO is great when you don 't know what variants
       | might exist, but it's useless when you DO want to control it. If
       | I as a developer know that the set of Payment options are
       | Credit/Cash/Invoice - then I want it exhaustively checked. I
       | deliberatel _do not want to leave that open_. I want to be able
       | to switch on the closed set of 3 cases, and be warned if I fail
       | to check a case. I want to be able to do this without inverting
       | the logic like and calling into the unknown like
       | paymentMethod.HandlePayment(order).
        
         | [deleted]
        
         | lukevp wrote:
         | I wonder if you could implement this all within a linter on top
         | of Rust or some other language? Some of your asks already exist
         | at that level (for example, TypeScript autocomplete in VS Code
         | knows which enums you haven't checked in a switch, and it also
         | knows if a property has been verified that you've narrowed via
         | a type union, so perhaps you could extend this to enforce
         | coverage of switch statements?
        
           | bombela wrote:
           | The Rust compiler already enforces that you match all
           | variants of an enum. And Rust Enum is basically a C union
           | with an integer tag.
        
         | jayd16 wrote:
         | I think you misunderstand the ECS pattern. You want:
         | class DrawSystem{             Draw(DrawComponent)         }
         | 
         | With some mapping of DrawComponent to MyEntity. That mapping
         | would most likely NOT be in the MyEntity class definition.
         | 
         | Or you can put the DrawComponents on the Draw system and simply
         | track mapping back to the MyEntity instance they correspond to.
         | New code lives in the systems.
         | 
         | In a game you'd have some kind of scene system that could track
         | what entity has what components and then you don't even need a
         | MyEntity class at all. You wouldn't track that composition in a
         | class.
         | 
         | The sugar that Rust is bringing is that it feels like the
         | implementation is being added to the instance instead of an
         | external system outside it, but you can make that work in C#
         | with some extension methods. It would be nicer if/when C# gets
         | extension interfaces.
        
           | alkonaut wrote:
           | Thats exactly how I understand an EC system but my issue with
           | it is the overly soft coupling with just ids.
        
             | jayd16 wrote:
             | I guess you want to know that every MyEntity can be drawn
             | and can move so you don't have to try-get the component.
             | 
             | The disconnect I think is that part of the ECS design is
             | that you have the freedom to break that guarantee. Entities
             | are no longer types but collections of components. The soft
             | coupling is the goal.
        
         | magicalhippo wrote:
         | I've only briefly looked at Rust. How would you handle your
         | Drawable if it needs to store some extra data?
         | 
         | Few of the things I use inheritance or composition for can be
         | implemented without storing some extra data.
         | 
         | As an example, say a buffered Stream, that takes a Stream
         | instance and adds buffered access. This BufferedStream would
         | need to store the buffer data somewhere.
        
           | coolreader18 wrote:
           | Drawable is just a trait, a definition of methods that can be
           | abstracted over. If you wanted to store data, you'd do it in
           | the type that Drawable is actually being implemented for,
           | i.e. MyEntity.                   struct MyEntity {
           | cache: RenderingCache,         }         impl Drawable for
           | MyEntity {             fn draw(&self, canvas: &mut Canvas) {
           | self.cache.draw_cached(canvas, |cache_canvas| {
           | cache_canvas.render(ASSET);                 });             }
           | }         const ASSET: Asset = load_asset("foo.png");
        
             | magicalhippo wrote:
             | Thanks, that is very clear.
             | 
             | Think Rust differs enough that it's a bit hard for me to
             | draw direct comparisons with my primary languages, but with
             | the emphasis on interfaces like that it does seem like a
             | nicer way to implement extension methods.
        
               | smt1 wrote:
               | Here is some nuance with Rust's OOP relative to
               | java/go/c++: https://stevedonovan.github.io/rust-gentle-
               | intro/object-orie...
               | 
               | If you know haskell, Rust's traits are very similar to
               | type classes, except it also has c++-like generics
               | (templates) and is primarily expression-based like ocaml.
               | 
               | traits vs haskell type classes:
               | 
               | trait -> class struct -> data instance -> impl
        
           | hderms wrote:
           | Rust has BufWriter/BufReader and I'm not an expert but I
           | think you'd just view it as extension through composition
        
             | magicalhippo wrote:
             | That does indeed seem[1] to do regular, naive as per OP,
             | composition.
             | 
             | I was just curious what OP was using these traits for, as
             | in my non-Rust experience seldom have the need to compose
             | stuff without storing additional data.
             | 
             | [1]: https://doc.rust-
             | lang.org/src/std/io/buffered/bufwriter.rs.h...
        
         | branko_d wrote:
         | > stack and heap as with C# structs and classes
         | 
         | Nitpick: C# struct can be on the heap (when it's "boxed").
         | 
         | The fundamental difference between C# class and struct is how
         | they treat assignment operation. The class has "reference
         | semantics" (assignment creates a new reference to the same
         | object) and struct has "value semantics" (assignment creates a
         | new object).
         | 
         | C# uses terms "reference types" (of which "class" is one) and
         | "value types" (of which "struct" is one) to make the difference
         | clear.
         | 
         | Structs being on the stack (most of the time) is just a
         | consequence of their value semantics, and should be treated
         | mostly as an implementation detail.
        
           | alkonaut wrote:
           | I'm aware of the differences between structs and classes,
           | boxing etc. (.net dev since '02), . My point is "class" can
           | some times be a faceless "value" with no identity, and some
           | times have an identity. The "records" feature is a way of
           | addressing this difference, but like everything else it's
           | tacked on a bit late. A value class, like a value type
           | (struct, enum), should/could be immutable, have structural
           | equality etc.
           | 
           | I'd like my language to be extremely explicit about whether
           | an object has identity or not. It ties into things like
           | ownership, move vs. copy, disposal etc too.
        
       | brokencode wrote:
       | I think this is really cool, and it elegantly brings together
       | some pretty advanced features from FP and OO into one purely OO
       | language. The only thing I'd like to see more about is how
       | immutability would be handled, because that is harder than it
       | sounds in OO.
       | 
       | And for all of you saying this is basically just Kotlin or
       | Swift.. do those languages have higher-kinded types or true
       | multiple inheritance? Not as far as I can tell. And you could
       | argue those aren't needed, but that does mean this is a different
       | language with potentially different pros and cons.
        
       | maxekman wrote:
       | How are Optional named arguments and Generics obvious choices for
       | a new OO language? Good to have, sure, but obvious?
        
       | [deleted]
        
       | Traubenfuchs wrote:
       | Swift? Typescript? Both fairly new and object oriented. Go?
       | Somewhat object oriented.
        
         | [deleted]
        
         | ibraheemdev wrote:
         | In what way is Go object oriented?
        
           | gher-shyu3i wrote:
           | It has everything except explicit inheritance, and embedding
           | covers some aspects of inheritance.
        
       | howinteresting wrote:
       | One of the fundamental issues with OO subtyping continues to be
       | how variance works. I wish the author spent more time talking
       | about how they envision variance.
       | 
       | My other big problem with OO is the spaghetti code that results
       | once you start mixing overloads and hooks across superclasses and
       | subclasses. The OO model really seems to be a little too low-
       | fidelity overall.
        
         | ar-nelson wrote:
         | I didn't really think about variance while writing the
         | examples; I assumed it would just work the way it does in Scala
         | and Kotlin (explicit variance annotations). I do wonder if
         | that's necessary, though--in theory, the compiler could infer
         | variance from how you use the type parameters, which is in a
         | similar spirit to the rest of the language.
         | 
         | What do you think are the major problems with variance in OO?
        
           | howinteresting wrote:
           | Explicit variance, especially for types, is just really
           | confusing. Implicit variance has the risk of breaking APIs or
           | overpromising a particular variance without developers
           | realizing.
           | 
           | If you have subtyping you need to think about variance.
           | That's led me to believe that subtyping's simplicity is
           | deceptive, and that it should be avoided as much as possible.
           | (Rust has subtyping but only for lifetimes, and it's
           | confusing enough there.)
        
         | Hermel wrote:
         | Contrary to popular belief, OO is not primarily about
         | inheritance. That's one of its distinct features. But
         | experienced software engineers tend to prefer composition over
         | inheritance. OO is about encapsulation and about putting
         | conventionally functions close to the data it operates on.
        
           | branko_d wrote:
           | I think this quote from Alan Kay is rather illuminating:
           | 
           |  _" The key in making great and growable systems is much more
           | to design how its modules communicate rather than what their
           | internal properties and behaviors should be."_
           | 
           | https://wiki.c2.com/?AlanKayOnMessaging
        
           | throw0101a wrote:
           | > _OO is about encapsulation and about putting conventionally
           | functions close to the data it operates on._
           | 
           | According to Alan Kay, the essential ingredients of OOP are:
           | Message passing, Encapsulation, Dynamic binding.
           | 
           | > _OOP to me means only messaging, local retention and
           | protection and hiding of state-process, and extreme late-
           | binding of all things. It can be done in Smalltalk and in
           | LISP. There are possibly other systems in which this is
           | possible, but I'm not aware of them._
           | 
           | * https://userpage.fu-
           | berlin.de/~ram/pub/pub_jf47ht81Ht/doc_ka...
           | 
           | * https://medium.com/javascript-scene/the-forgotten-history-
           | of...
           | 
           | * https://softwareengineering.stackexchange.com/questions/465
           | 9...
           | 
           | Though it's recognized that Simula invented objects:
           | 
           | * https://www.hillelwayne.com/post/alan-kay/
        
             | agumonkey wrote:
             | One think you may get (but I may speculate here) from his
             | talk is that he had a way more abstract / mathematical view
             | of systems and programming. I think he didn't see OO as
             | isolating tasteful procedure more than providing interfaces
             | and concepts to compose concept cleanly.
        
           | agumonkey wrote:
           | Is it funny or sad that after 30+ years, the industry is not
           | clear on what is or is not OO.
           | 
           | At a gig in my college cs lab, teachers talked about OO, 3
           | teachers, no answer the same.
        
           | wvenable wrote:
           | Inheritance is very useful in OO but it's all well-tread
           | ground now. The things that have is-a relationships are all
           | well defined and part of libraries and frameworks that have
           | been around for decades.
           | 
           | As a high-level app developer, one has much less use for is-a
           | relationships but what we do sits on top of that well-tread
           | inheritance filled OOP environment.
        
           | SomeHacker44 wrote:
           | Which begs the question: why use OO at all if you prefer
           | composition? I personally am "over" OO, even though I have
           | been doing OO professionally for decades.
        
             | invisiblerobot wrote:
             | Exactly. Polymorphism is the killer feature and it's
             | expressed more simply in functional languages like Clojure.
        
             | mikewarot wrote:
             | >why use OO at all if you prefer composition?
             | 
             | OO is a way to bundle a structure with the methods to
             | manage it.
             | 
             | Inheritance is a way to manage the need to customize an
             | object.
             | 
             | Composition is a way to bundle objects.
             | 
             | Why wouldn't you do both? That's what Delphi did back in
             | the 1990s when they built the Visual Component Library.
             | (VCL)
             | 
             | You build your forms in the GUI builder, which composed a
             | form object out of various components which were all
             | eventually derived from tObject. It still lives on, and
             | Lazarus implements something similar for Free Pascal.
        
             | blowski wrote:
             | Over the last 30 years, object oriented languages got more
             | attention, more funding, more companies using them, more
             | jobs available, more developers, more education resources,
             | more libraries.
             | 
             | In short, I use OO because everyone else does. Now in my
             | 40s, I'm not going to change merely because another
             | language is more expressive.
             | 
             | But yeah, if I could do it all over again, I'd probably
             | choose Clojure.
        
           | howinteresting wrote:
           | Ehhh, inheritance hiearchies are the well-worn grooves of OO,
           | the paradigm encourages them even if it doesn't strictly
           | require them. I don't buy this argument.
           | 
           | Typeclasses are a much better way to do polymorphism.
        
       | michaelbrave wrote:
       | what the author is proposing sure sounds an awful lot like Swift
       | to me.
        
       | mangopi wrote:
       | You basically want Go, but you haven't learned it yet.
        
       | vanderZwan wrote:
       | Regarding the "Kingdom of Nouns", I always thought Lobster[0][1]
       | had a really cute idea: x(a, b, c) and a.x(b,c) are equivalent.
       | Don't know if that is original to the language, but it's where I
       | saw it first. It only really makes sense with the other features
       | of the language, though.
       | 
       | (Also, I just discovered that it now targets WASM. Maybe I should
       | give it another go!)
       | 
       | [0] http://strlen.com/lobster/
       | 
       | [1] https://github.com/aardappel/lobster
        
         | Snarwin wrote:
         | In D, this is called "uniform function call syntax." [1] It's
         | really handy, not just for OOP but also for pipeline-style
         | functional programming, since it lets you compose functions
         | from left to right.
         | 
         | [1] https://tour.dlang.org/tour/en/gems/uniform-function-call-
         | sy...
        
         | ar-nelson wrote:
         | Author here: unified function call syntax is awesome but sadly
         | out of scope for what I was trying to do with this language. I
         | mention this in the "Class-based discoverability" section: for
         | the sake of uniformity and IDE discoverability (arguably Java's
         | killer feature that its successors have diluted), I'd want
         | every value and function to belong to a class, which means
         | every nonlocal method call should come after a dot.
        
           | vanderZwan wrote:
           | This already assumes that unified call syntax would
           | inherently get in the way of that - do you have any reason to
           | believe that?
           | 
           | I mean, Lobster has classes, or at least something that it
           | calls classes:
           | 
           | https://aardappel.github.io/lobster/language_reference.html#.
           | ..
        
         | jayd16 wrote:
         | Would be kind of neat if the language supported returning
         | tuples or anonymous types. ie x(a,b,c) is equivalent to
         | {a,b}.x(c)
        
         | tekknolagi wrote:
         | This is called unified function call syntax
        
           | vanderZwan wrote:
           | You know, now that you mention it, I think every time I
           | mention this feature of Lobster someone explains this to me,
           | and for some weird reason I keep forgetting. Thanks for
           | enlightening me again, hopefully it'll stick this time!
        
         | klibertp wrote:
         | Nim also has this. You can also leave out the parens from
         | calls, which adds to the syntax flexibility.
        
         | thesuperbigfrog wrote:
         | Ada supports that syntax for tagged types:
         | 
         | https://learn.adacore.com/courses/intro-to-ada/chapters/obje...
        
       | brianberns wrote:
       | F# is functional-first, but also fully supports OO. It's a good
       | balance for the 20's, or any other decade.
        
         | dfgdghdf wrote:
         | Agreed. F# lacks the HKT that the OP calls for, but it is
         | possible to write plenty of pragmatic functional code without
         | them. They might land soon though.
        
           | iso8859-1 wrote:
           | What's your source for F# getting HKT's soon?
        
             | dfgdghdf wrote:
             | C# has a HKT proposal now. If C# gets it, F# will soon
             | follow.
        
         | dunefox wrote:
         | It seems like a great language; I'm going to look into it this
         | year - maybe for advent of code or some data science/ML stuff.
         | For some annoyances there is F#+:
         | https://fsprojects.github.io/FSharpPlus/
        
       | gigatexal wrote:
       | Ugh. Another reminder that high level OO languages and things
       | make my head hurt.
        
       | fit2rule wrote:
       | All of this can be done in Lua.
        
       | PaulHoule wrote:
       | Bring back Turbo Pascal!
        
       | dale_glass wrote:
       | Can we get rid of 'sealed'? I really hate that keyword. Sometimes
       | there's a good reason for it, sure, but sometimes it's used
       | gratuitously, and then I have to work around it for no good
       | reason.
       | 
       | Eg, in C#, SqlDataReader is sealed. Which means I have to do
       | this:
       | reader.GetString(reader.GetOrdinal("FirstName"));
       | 
       | When I just want to skip the verbosity and be able to do this:
       | reader.GetString("FirstName");
       | 
       | So the logical thought there would be just to inherit from it,
       | add an extra overload to those functions, and life got more
       | comfortable, plus you can still pass it to anything that wants a
       | SqlDataReader. But oops, you can't do that, because somebody at
       | Microsoft decided the interface for this thing was perfect is not
       | to be messed with.
       | 
       | If there's something that really gets me annoyed is when I run
       | into a limitation that seem completely arbitrary but intentional.
       | It's not there because of the technical limits of the hardware,
       | or because the compiler isn't clever enough, but purely because
       | somebody intentionally decided 'nope, you don't get to do this
       | particular thing you can do all day otherwise'.
        
         | jasode wrote:
         | _> Can we get rid of 'sealed'? I really hate that keyword.
         | Sometimes there's a good reason for it, sure, but sometimes
         | it's used gratuitously, and then I have to work around it for
         | no good reason._
         | 
         | Fyi... this stackoverflow Q&A has opinions on why 'sealed' is a
         | rational default for the person who designed the class:
         | https://stackoverflow.com/questions/268251/why-seal-a-class/
         | 
         | It doesn't mean the programmer thinks the base class is
         | "perfect". Instead, the author has _not deliberately designed_
         | the particular class for inheritance and the  "sealed" keyword
         | expresses that.
        
           | dale_glass wrote:
           | Yes, I know there are good reasons to do it sometimes, but
           | still don't get what about SqlDataReader benefits from it,
           | other than "I don't know why would anyone would want to
           | inherit from it".
           | 
           | Looks like my issue with that got fixed with extension
           | methods, which should let me tack on such improvements
           | whether the original designer of the class thought that would
           | be a good idea or not.
        
         | [deleted]
        
         | zackmorris wrote:
         | I second removing sealed (and final) from all languages (except
         | maybe for embedded/microcontroller work where implementation
         | details matter).
         | 
         | This issue might not seem relevant in your own projects, where
         | maybe you want to seal a class so a junior developer doesn't
         | break something.
         | 
         | But it's disastrous for library and framework development.
         | Here's an open question of mine related to this:
         | 
         | https://stackoverflow.com/questions/65852139/listen-for-chan...
         | 
         | In that case, I'm trying to write a script that people can
         | attach to a game object to rebuild metadata after a mesh is
         | changed. I need the metadata for my shader, since it needs info
         | about neighboring vertices, which would only be available in
         | geometry shaders (which have fallen out of fashion since they
         | run at the wrong stage of the rendering pipeline).
         | 
         | That way my shader would "just work" without making the user
         | have to remember to set a flag or send an event when something
         | changes (which is not future-proof).
         | 
         | Normally I would just inherit from the Mesh class,
         | override/extend methods like Mesh.SetVertices(), and then
         | explain how to use my class in the readme. Better yet, I would
         | use something like inversion of control (IOC) to replace the
         | project's global Mesh class with my own (this is how frameworks
         | like Laravel work, or try to work). In fact, there are half a
         | dozen ways of accomplishing this, perhaps more.
         | 
         | But with sealed and final, I'm just done. There is no
         | workaround, by design. Because C# inherited this kind of
         | opinionated thinking (anti-pattern) from Java. And Unity uses
         | C#, so unwittingly fell into this trap since so many of their
         | classes are sealed. Which all reduces languages like C# and
         | Java to being toy languages, at least for someone like me
         | working from first principles under some approximation of
         | computer science.
         | 
         | Maybe I should write to Unity and explain that all of this
         | happened during the very first interesting thing I tried to do.
         | Or maybe we could get rid of this concept altogether and avoid
         | the problem in the first place.
         | 
         | Even the gospel of Martin Fowler tends to agree with my stance
         | on this:
         | 
         | https://martinfowler.com/bliki/Seal.html
        
         | RedNifre wrote:
         | You could do this with an extension method, no inheritance
         | required.
        
           | dale_glass wrote:
           | Oh, that's very nice, and just the kind of functionality I've
           | been wanting for some time.
           | 
           | That was something I was recalling from a very old project
           | though. I'm not sure right now when that was exactly. Maybe
           | it wasn't in the language yet at the time, or was a new
           | feature still and I didn't find about it, or it wasn't in
           | Mono yet.
           | 
           | I've not done C# in a long time, so no doubt I missed a lot
           | of developments.
        
       | Graffur wrote:
       | why no nulls? and no exceptions? I am no language designer but as
       | a user they seem ... usable.
        
         | noir_lord wrote:
         | If you have nulls (or nullable types) then you end up checking
         | for them everywhere (in every codebase I've seen anyway -
         | keeping them on the boundaries only works _if_ that 's
         | practical and the language always does it for it's standard
         | libraries etc).
         | 
         | Additionally you can no longer say Thing is Thing, it's always
         | Thing or null - which results in special handling
         | 
         | if (foo === null){} everywhere and when you don't boom
         | NullPointerException.
         | 
         | Exceptions cause problems in other ways (especially when they
         | are allowed to escape up the stack), business logic shouldn't
         | be handling FileNotFoundException that was thrown 6 layers
         | away.
        
           | skwirl wrote:
           | >Exceptions cause problems in other ways (especially when
           | they are allowed to escape up the stack), business logic
           | shouldn't be handling FileNotFoundException that was thrown 6
           | layers away.
           | 
           | So instead all 6 layers should have to know about it in
           | addition to business logic? In most cases you can't really
           | ignore the fact that a file didn't exist in your business
           | logic.
        
           | dkjaudyeqooe wrote:
           | That's an argument for exceptions. If they shouldn't be
           | handling it they shouldn't be catching it. If they're the
           | highest level why didn't the other 6 layers handle it?
           | 
           | It's easier to be adaptive with error handling with
           | exceptions, and who's going to (correctly) test for all
           | possible errors on every call?
        
           | larzang wrote:
           | So what's your proposed alternative that doesn't involve
           | checking? Option-style wrappers enforce consistent checking
           | and handling, but so do explicitly nullable type signatures.
           | At some point you're going to need to know whether you have
           | data or not, and that requires checks.
        
           | TeMPOraL wrote:
           | > _business logic shouldn 't be handling
           | FileNotFoundException that was thrown 6 layers away_
           | 
           | No, that's the job of whoever requests a file to be read. But
           | what they may do in reaction to this, or discovering the file
           | is empty, or has malformed data, is to throw a
           | DataUnavailable exception, a subtype of RequestError. This is
           | something appropriate places of the business logic up the
           | stack may be interested in.
           | 
           | Exception types follow the same rules as "regular" user-
           | defined types: they're relevant in a subsection of a program,
           | and shouldn't propagate below that subsection.
        
         | orthoxerox wrote:
         | Exceptions are not a part of your method signature, so every
         | time you call something you don't know if you have to catch an
         | exception or not.
         | 
         | Java tried to fix that, but doesn't let you generalize over
         | exceptions, "my method throws FooException and whatever
         | exceptions method Bar on the instance of Glorbable you're
         | passing me throws".
         | 
         | Nulls have the same problem. Every time you get an instance of
         | Foo you don't know if it's a null or an object. C# is trying to
         | migrate to explicit nullability, but it's hard to convert an
         | established ecosystem with idiosyncratic nullability rules.
        
           | pjmlp wrote:
           | > Java tried to fix that
           | 
           | Nope, Java picked the idea from CLU, Modual-3 and C++, in
           | what seemed the way forward to declare exceptions.
           | 
           | It does provide both checked and unckecked exceptions,
           | unfortunely it suffers from bad learnings and quick hacks to
           | shut the compiler instead of thinking it through when
           | designing class libraries (yes even the std suffers from
           | this).
        
           | wvenable wrote:
           | Assume all methods can throw exceptions. And if, by chance,
           | you believe a method doesn't throw an exception today you
           | have no idea if it's implementation will change tomorrow and
           | it will then throw an exception.
           | 
           | Whether a method throws an exception or not doesn't matter as
           | much as people think it does. You can rarely recover from an
           | exception in the immediate parent calling method anyway. More
           | than likely the error will occur in a stack 20 layers deep
           | and recovery is to restart the entire operation from the
           | start.
           | 
           | Nulls are useful but I totally agree that they should be opt-
           | in in the type system.
        
           | brabel wrote:
           | > ... but it's hard to convert an established ecosystem with
           | idiosyncratic nullability rules.
           | 
           | Dart has just done that, though admitedly, it did not have an
           | ecosystem as large as C#'s.
        
             | orthoxerox wrote:
             | C# had to invent attributes to work around structs vs
             | classes and around TryX methods where out variables are
             | null or not based on the return value. And the issue of
             | arrays of objects or objects inside structs hasn't been
             | resolved yet.
        
         | dkjaudyeqooe wrote:
         | They live in a world where nothing ever goes wrong and nothing
         | is ever missing.
         | 
         | Sounds nice.
        
           | dunefox wrote:
           | It's almost as if nulls and Exceptions aren't optimal for
           | modelling missing states and faulty logic.
        
         | samatman wrote:
         | From an ADT perspective, letting any type T be null is the same
         | as saying everything is an Option<T>.
         | 
         | Except nothing else in the language forces you to handle the
         | optional case, so you end up with either null checks
         | everywhere, or your code blows up with NullPointerExceptions,
         | or sometimes both.
         | 
         | Ever been deep in a Java call stack, and wondered if you've
         | already checked for null in all cases? In a language with
         | (only) Option types, you can simply declare that variable to be
         | T, and if it's actually Option<T> (i.e. you haven't checked for
         | absence along some path) you'll get an error. A good compiler
         | will even tell you where in the code the None is sneaking in,
         | so you can fix it there, or pattern-match on Some and None
         | where the error is, depending on which is correct for your
         | logic.
         | 
         | I'm ignoring the exceptions question, because the post doesn't
         | even consider a condition/restart system, which means it's not-
         | even-wrong.
        
       | booleandilemma wrote:
       | _Object-oriented programming is out of fashion now_
       | 
       | What world does this person live in?
        
       | [deleted]
        
       | [deleted]
        
       | alfiedotwtf wrote:
       | Brain: haha why would they need a programming language for the
       | 19OH THEY'RE TALKING ABOUT THIS DECADE
        
       | TeMPOraL wrote:
       | Hard disagree on exceptions. Passing sum types types along with
       | every call is annoying - in a good codebase, probably upward of
       | 50% of your code might be returning some kind of Result/Expect
       | type. Exceptions can be done right, and there are solutions to
       | the main objections (implicit, hard to recover from) in various
       | languages - but somehow nobody seems to have managed to put all
       | of them in the same language.
       | 
       | Here's what I want from a new OO language, exception-wise:
       | 
       | - Checked exceptions only. Every function that can possibly
       | throw, must declare what it throws. Supertypes may be used in
       | declarations to cover a whole family of exception types. Every
       | function must either handle or explicitly declare exceptions that
       | can be thrown from its callees. This is to be _baked into type
       | system and enforced at compile-time_.
       | 
       | - If the language is driven with IDE use in mind, allow "auto"
       | for checked exception declarations. Will cut down on line noise,
       | at the expense of readability (as type deduction always does).
       | But since exception declarations are resolved at compile time,
       | you won't be able to make an actual mistake here.
       | 
       | - A _condition_ system, not _exception_ system. Extend the out-
       | of-band signalling mechanism to be used for arbitrary things, not
       | just  "exceptional situations". I.e. just as I can say `throw
       | SomeError{someData, someMessage}`, I want to also be able to do
       | e.g. `throw Progress{percentage, total}` to feed a progress bar
       | that's declared few layers above in the stack (which would just
       | execute its code and return control to the thrower; no stack
       | unwinding). This is what you have in Common Lisp.
       | 
       | - Stack winding, not just stack unwinding. Also from Common Lisp,
       | I want the exception (condition!) handler to happen prior to
       | stack unwinding, in a way that would allow me to truly recover
       | from the problem and resume execution where the condition was
       | thrown, or somewhere in the middle of the call stack, between the
       | handler and the thrower.
       | 
       | - Separating signalling, handling and recovery, both conceptually
       | and in code. Again, from CL's condition system. A thrower throws
       | an exception (condition), a handler decides what to do (or
       | rethrows), and one of the things it can do is pick a "restart"
       | declared down the call stack - then stack is unwound only to the
       | point of that restart, and control resumes from there. Note the
       | programmatic ability to choose a restart. Not 100% sure how to
       | handle it in a type-safe way, but I believe it could be done.
       | 
       | - All the other stuff from Common Lisp's condition system.
       | 
       | So basically, a statically typed blend of C++, Java and Common
       | Lisp, mashing together their best features into a coherent and
       | powerful system.
        
         | kidfrommars wrote:
         | So more like an algebraic effect system than what most people
         | think of as exceptions? I definitely think that's a promising
         | area, but unfortunately it's currently still only really a
         | thing in research experiments a-la Koka
         | (https://www.microsoft.com/en-us/research/project/koka/).
        
       | nesarkvechnep wrote:
       | No mention of message passing? Shame.
        
         | tomp wrote:
         | Why would you want message passing in the language (as opposed
         | to a library)?
        
         | ar-nelson wrote:
         | How does message passing work with a statically-typed language?
         | (Genuine question, not criticism.) My understanding is that
         | Smalltalk-style message passing's main advantage is that it
         | allows handing or proxying unknown method calls, but unknown
         | method calls are forbidden in a static type system.
        
           | weatherlight wrote:
           | Make objects Actors (as per the Actor model of computation).
           | You don't call methods on objects, you pass a message to
           | their mailboxes, Objects then choose how to process those
           | messages. To do this though, messages would have to be
           | immutable.
        
       | transfire wrote:
       | CLOS (https://lispcookbook.github.io/cl-cookbook/clos.html)
        
       | wongarsu wrote:
       | > But, if we made a new statically-typed OO language from scratch
       | in 2021, something in the vein of Java or C#, taking everything
       | we've learned from functional programming and a decade-plus of
       | scathing OO criticism, could we fix this?
       | 
       | Modern C# is already a statically-typed OO language that takes
       | lots of lessons from functional programming and decades of
       | scathing OO criticism (after all it's basically "Java done
       | right"). And of course there's also Scala and F# if you want
       | something more functional than OO.
       | 
       | I think the author summarizes it best at the end: "I realize
       | there's not much need for a language like this--the space is
       | crowded enough as it is--but it's fun to dream.".
        
         | pjmlp wrote:
         | One of the best OO languages out there is Eiffel, value types,
         | DbC, MI, generics, lambdas, non-nullable references, a very
         | nice graphical editor, JIT for development and AOT toolchain
         | that pings back into the C and C++ compilers of the host
         | platform.
         | 
         | Java and .NET are still catching up to what it offered in 2000.
         | 
         | Sadly it never got a big name sponsor to push it.
        
           | le-mark wrote:
           | Ocaml also has a long standing claim to being an oo plus
           | functional language for the future that hasn't seen a lot of
           | uptake. It would have been heartening and reassuring for the
           | author to explore more candidates than Scala.
        
             | sfRattan wrote:
             | The complaints I remember about ocaml the last time I
             | checked it out were related to multithreading. No idea if
             | that's changed. But I do see ocaml very often in use to
             | write a compiler, especially for languages that aren't yet
             | self-compiling, most recently when looking into Haxe.
        
               | cptwunderlich wrote:
               | Not yet, but it's a work in progress:
               | https://discuss.ocaml.org/t/multicore-ocaml-
               | february-2021/74...
        
           | SloopJon wrote:
           | > Sadly it never got a big name sponsor to push it.
           | 
           | Has Eiffel had a high-quality open source implementation? My
           | recollection is that Eiffel Studio's software is proprietary
           | and expensive.
        
             | Jtsummers wrote:
             | You can download Eiffel Studio and use it for free. I'm not
             | sure what restrictions there are or capabilities that may
             | be removed. It's included in Homebrew (mac) but it's an
             | older version. Macports seems to have the current version
             | available. Not sure about various Linux distros.
             | 
             | https://dev.eiffel.com/Main_Page
        
         | ghosty141 wrote:
         | The biggest problem for me is the age old ,,null".
         | 
         | C# with monads would be great.
        
           | iso8859-1 wrote:
           | Lack of null and monads are kinda orthogonal. Yes, you can
           | use monads to model "early return", but you can use them for
           | so many other things. F# already offers computation
           | expressions, which you can use for monads (but with no
           | typing). But even Haskell provides no enforcement of the
           | monad laws. Surely you must know about F#, so your comment
           | makes little sense to me. If you want HKT's in F#, why not
           | just say so?
        
           | franknine wrote:
           | Highly recommend Functional Programming in C#:
           | https://www.manning.com/books/functional-programming-in-c-
           | sh... If you want to learn how to do some functional in C#.
           | 
           | If you just need some ready-made monads, language-ext is the
           | way to go: https://github.com/louthy/language-ext
           | 
           | We tried to bring some functional ideas into our Unity3D
           | codebase with help of these resources, it's hard but doable.
        
           | mdpopescu wrote:
           | Something which I've used in the past (edited because I
           | spotted some mistakes while re-reading the code [grin]):
           | public readonly struct Option<T>         {             public
           | static readonly Option<T> NONE = new Option<T>();
           | //                  public T Value { get; }
           | public bool HasValue { get; }                  public
           | Option(T value)             {                 Value = value;
           | HasValue = value is { };             }
           | public Option<TR> Select<TR>(Func<T, TR> selector) =>
           | HasValue ? new Option<TR>(selector(Value)) : Option<TR>.NONE;
           | public Option<TR> SelectMany<TR>(Func<T, Option<TR>>
           | selector) => HasValue ? selector(Value) : Option<TR>.NONE;
           | public T OrElse(T defValue = default) => HasValue ? Value :
           | defValue;         }
           | 
           | You can of course add more utility functions besides these,
           | eventually as extension methods, but it's a starting point.
        
             | ryanjshaw wrote:
             | You can also use a library like LanguageExt. In practice
             | I'm torn between how neat and logical it looks, and wasting
             | 30min figuring out I used the wrong match() on a mixed
             | sync/async scenario after a method signature changed and
             | that's why my database context is disposed of randomly in
             | the middle of operations.
        
           | jayd16 wrote:
           | You can set references to default to non-nullable now with
           | compiler flags or some such thing.
        
         | orthoxerox wrote:
         | > Modern C# is already a statically-typed OO language that
         | takes lots of lessons from functional programming and decades
         | of scathing OO criticism (after all it's basically "Java done
         | right").
         | 
         | C# 1.0 was based on Java 1.3 and modern C# has inherited a lot
         | of its warts and added some of its own, like delegates and
         | events.
        
           | astrange wrote:
           | And Java is based on Objective-C, except without the good
           | ideas like messages and with new bad ideas like checked
           | exceptions.
        
             | erik_seaberg wrote:
             | The problem is that way too much of stdlib declares no
             | checked exceptions, which forces any useful code to smuggle
             | them out in unchecked ones. Generic types in particular
             | need exception lists; I should be able to take a
             | Function<T, R, X1, X2, ...> arg and imply that I throw
             | whatever it throws.
             | 
             | Though nowadays I think maybe monadic Result<T, X> is the
             | way to go.
        
         | vips7L wrote:
         | IMO Java has learned from the mistakes of C# as well. See
         | Task<T> vs Loom's virtual threads. Brian Goetz (the Java
         | language architect) himself even said they're waiting to see
         | how C#'s nullable types plays out before considering them in
         | Java.
        
           | jayd16 wrote:
           | Async/await and implicit thread scheduling solve different
           | problems. Loom is more about catching up to Go and Kotlin,
           | no?
        
             | anoncake wrote:
             | Not Kotlin, AFAIK Loom is about the JVM.
        
             | tybit wrote:
             | They mostly solve the same problems, albeit in very
             | different ways. Loom is primarily about making threads
             | cheaper, async await is primarily working around threads
             | being too expensive.
        
           | lostmsu wrote:
           | Yeah. It's kinda funny. Due to extensive F# -> C# -> Java
           | feature bleed you can consider them beta channels of the same
           | language. Java being LTS, C# - main, and F# - Dev Builds.
        
       | lostmsu wrote:
       | I love C#, but they seriously fucked up Span<T> and Range by
       | restricting them to 32 bit length, which by the time when the
       | features were concepted was already extremely niche.
       | 
       | Now language is very clunky to work with in ML/"big" data space.
       | 
       | Technically, that is the fault of the runtime, which does not
       | support 64 bit arrays, but naturally teams must be working
       | together.
        
       ___________________________________________________________________
       (page generated 2021-03-13 23:01 UTC)