Live data from Hacker News

In defense of complicated programming languages

viralinstruction.com

271–280 of 379 posts

Re: In defense of complicated programming languages

#271
post #17

> In video game design there is a saying: Show locked doors before you show a key This is something I've tried putting into words many times. I'll try to solve a problem and get to know its challenges deeply. Then a tool is introduced that brings it all together. In these cases, I seem to quickly get a full grasp of the operation and essence of the tool. I wish education was based around this principle. A bit like wh…

> Then a tool is introduced that brings it all together. I totally agree there and I think it's due to the fact that you've got a mental model of the problem space and your brain can see the empty hole. Once you have a tool of that shape, your brain knows where it goes and how it solves your problem.

This is very close to the image I have in my head. I visualize it as a puzzle: The more pieces you have, the more naturally more pieces come and the easier it becomes to see what you need and what is out of place.

Re: In defense of complicated programming languages

#272

Earlier quoted context omitted.

Closures are the superior mechanism though, you can implement classes plus all the crazy decorators / metaclasses / mixins / etc. The reverse is true of course but in the dumbest way possible. A closure-based implementation of a class is just a function that returns a structure full of closures (an interface if you will) all with the same shared state. But now because a class is just a function, you can do all kinds…

> Classes are not just unnecessary, they're a bad abstraction. And yet, there are approximately 0 examples of the "closure-backed classes" example you give, while every vaguely popular language except C has some variant of polymorphic classes, from StandardML to Lisp. Your closure example is not used in any langauge because it's extremely un-ergonomic. If the langauge doesn't have some kind of concept of "interface"…

Using C, which doesn't even have closures, as an example of why this doesn't work is rather silly. How can you have closure-based classes without closures? The ergonomics are beyond horrific in this case but I don't see how that's relevant.

> And yet, there are approximately 0 examples of the "closure-backed classes" example you give, while every vaguely popular language except C has some variant of polymorphic classes, from StandardML to Lisp.

SML is actually one of the best languages to implement this approach! And as far as I know this is even the recommended way to do it (see the MLton docs). No idea what polymorphic classes you're thinking of when it comes to SML. Nobody uses objects in OCaml either.

> Your closure example is not used in any langauge because it's extremely un-ergonomic.

I'm not arguing ergonomics, just implementation details, you can add all the syntax sugar you want :) Also Erlang Actors are functions with an explicit message loop. So that's even lower level than this.

> If the langauge doesn't have some kind of concept of "interface" (abstract class, type class, trait, structural typing etc), then you'll either miss polymorphism (return a single specific type), or be too dynamic (return an object that could do anything).

I don't follow, neither of those are true. Yes you return a single type, that single type is the interface type (which can have infinite implementations, just like in any language where you return an interface). But you don't need a specific concept of an interface, it's just a record of closures.

Also every language should have a way to say "this struct is the union of these structs", which gives you the subtyping you're typically missing with this approach (i.e. an object that implements N "interfaces" can be cast to all of them).

Without that you need to add an extra layer of indirection with explicit casting functions. Not ergonomic as you say.

> It also risks being inefficient if you try to naively add function pointer members to each object to support runtime dispatch.

If you're not using function pointers then you don't have runtime polymorphism, what do you think a vtable is? It's a struct of function pointers. An object is a pair (voidptr, vtableptr), where the vtable can be created at compile time or at program start. Of course that's not very ergonomic, but I'm not arguing ergonomics.

> The closure-based alternative also obscures the relationship between product data types (structs/record types) and classes (structs with methods).

Classes obscure the relationship between subtyping, interfaces, inheritance, abstract datatypes and modules. A class is much more than a struct with methods. A struct with methods is vtable.

> Closure-backed classes are a neat experiment, but just like manual virtual table management in C, they are not a realistic solution to the problems that classes address, if you can possibly avoid them.

What problems do classes address? Nobody seems to have an answer to this, which is why modern languages like Rust and Go don't have them. You can argue that traits and interfaces are are "OO", but they're not classes. Rust doesn't let me do Iterable.new{...} at runtime, exposing it's nature as a struct of functions, but nothing says it couldn't.

Re: In defense of complicated programming languages

#273

"Classes would still exist, but as implicit patterns." Classes are not a design inevitability, but just one way of managing state. Languages that mostly avoid mutable state don't tend to have object systems, for instance.

> Classes are not a design inevitability, but just one way of managing state What "Classes" are is a tragedy of things. Classes are closures, with a syntactic form that allows for nested composition. This is a particularly brittle form and these qualities are found in all languages with Classes. Language maintainers, as they currently exist, have failed to concede that this is a bad idea, because "it works good enoug…

>> Languages that mostly avoid mutable state don't tend to have object systems, for instance.

> They do, in the form of closures.

You can build an object system from closures, certainly, but I wouldn't say closures are objects by themselves. An closure lacks inbuilt messaging, which is usually considered a requirement for an object.

Re: In defense of complicated programming languages

#274
post #233
post #211

Earlier quoted context omitted.

If you don't have any imagination and you treat dogs as database entries then yes you might do just that. If you are only person writing it and using that code, the same. You did not address interaction between dogs. Some other idea, how do you pass dogs to a playpen, how would they interact - maybe you want different playpens where you can send your dogs into, some playpens having food, some not having a food, diffe…

Well respectfully, you provided the original project spec as an example of where objects were required, and I demonstrated that no, they mostly weren't. I agree that it's unproductive for you to keep adding things to the spec so that I can demonstrate that nope, you still don't need objects to do all that. All that objects are, are functions glued to data. That's it. Whether or not you choose to glue them together, t…

Link to YouTube was really nice.

I would not agree that one cannot change the class definition as it really depends on the system one is writing. I change class definitions on daily basis in business line applications. If I would be making frameworks then maybe I would not do that so often.

Re: In defense of complicated programming languages

#275
post #23

"Classes would still exist, but as implicit patterns." Classes are not a design inevitability, but just one way of managing state. Languages that mostly avoid mutable state don't tend to have object systems, for instance.

Type classes and ML functors are also a way to do polymorphism and dynamic dispatch on data structures. So which languages are left without object systems?

Polymorphism and dynamic dispatch don't equate to an object system in and of themselves.

At minimum, I'd say an object system needs some form of messaging and state hiding. That is, an object holds hidden state, and responds to external messages.

Re: In defense of complicated programming languages

#277
post #236

Earlier quoted context omitted.

I think that's the difference between computer science and programming. Yes, you'll be a better programmer if you focus on the low-level details, but you'll be a worse computer scientist. I guess, if your goal is to write optimizations, focus on details. If your goal is to find solutions or think creatively, focus on abstractions. Obviously, there’s a lot of overlap, but I’m not sure how else to describe it.

I disagree that you'll be a worse computer scientist. Computer science doesn't happen in Plato's heaven, it happens in real processors and memory. I tend to think focusing on abstractions is almost always the wrong approach. Abstractions should arise naturally as a solution to certain problems, like excess repetition, but you should almost always start as concrete as possible.

I would disagree with where you are drawing that line. I would say that computer science does happen in Plato's heaven; software engineering happens in real processors and memory. But most of us are actually software engineers (writing programs to do something) rather than computer scientists (writing programs to learn or teach something).

Re: In defense of complicated programming languages

#278
post #102

Earlier quoted context omitted.

Ah, doesn’t it just fly over to the nearest “catch”? Btw, the worst misunderstandings I’ve seen were not lacking knowledge, they actively believed in some magic that isn’t there if you dig deeper. That’s why I still think that teaching at least basic assembler is necessary for professional programming. It can’t make you a low-level bare metal genius, but it clears many implicit misconceptions about how computers real…

Stack unrolling is a complicated process. It's tempting to think of them as a kind of return value, but most languages do not represent them this way. (I believe it's a performance optimization.) Flying to the nearest catch can also be complicated, as it's a block, that involves variable creation, and this possible stack changes. Again it's easier to model as a normal block break and then a jump, but that's not the u…

I always expect them to use (thread-) global state, not unlike good old errno just more structured. There's always at most one bubbling up so that's how I would do it.

Re: In defense of complicated programming languages

#279

Earlier quoted context omitted.

> Classes are not just unnecessary, they're a bad abstraction. And yet, there are approximately 0 examples of the "closure-backed classes" example you give, while every vaguely popular language except C has some variant of polymorphic classes, from StandardML to Lisp. Your closure example is not used in any langauge because it's extremely un-ergonomic. If the langauge doesn't have some kind of concept of "interface"…

Using C, which doesn't even have closures, as an example of why this doesn't work is rather silly. How can you have closure-based classes without closures? The ergonomics are beyond horrific in this case but I don't see how that's relevant. > And yet, there are approximately 0 examples of the "closure-backed classes" example you give, while every vaguely popular language except C has some variant of polymorphic class…

A class is any construct that can:

- expose multiple function (implementations) as a single unit

- be chosen at runtime as a representation of a contract (polymorphism)

- (optionally) encapsulate some state accessible only to the functions of the class

This construct comes up naturally in a wide variety of programming tasks, mainly related to the large-scale organization of code.

Here are some examples of language constructs that are classes (perhaps by other names):

- `class` in C++, Java, C#, OCaml etc.; polymorphism achieved through interfaces/pure-virtual classes

- `class` in Python, Ruby; polymorphism achieved through dynamic dispatch

- `defmethod`+`defclass` in Common Lisp CLOS; polymorphism achieved through `defgeneric` (a rare example where polymorphism is at the single function level, instead of a bundle of functions)

- `structure`&functors in SML; polymorphism is achieved through `signature`

- `module` in OCaml; polymorphism achieved through the use of `module types`

- `struct` in Go; polymorphism achieved through use of `interface`

- `struct` in Rust; polymorphism achieved through `trait`

- `data` in Haskell; polymorhpism achieved through `class` and `instance`

- `typename` in the C++ template meta-language; polymorphism achieved through dynamic typing (SFINAE) or `concepts`

Now, some of these languages/constructs may have additional properties. Some support subtyping, some support multiple fields etc. Some explicitly implement interfaces, some implement them implicitly. But they all have the initial three properties in common, and recognizably solving the exact same problem in slightly different ways.

The implementation varies quite significantly, but I would be surprised if even one of these languages actually implements this construct using closures.

Now, to address some of the other points:

> If you're not using function pointers then you don't have runtime polymorphism, what do you think a vtable is?

I have often seen proposals to implement runtime dispatch via something like:

  struct Obj {
    foo: fn () -> int
    bar: fn int -> int
    field1: int
  }
This is wildly inefficient compared to the vtable approach, which is closer to

  struct ClassA { //vtable
    foo: fn () -> int
    bar: fn int -> int
  }
  struct Obj {
    vtable: &ClassA
    field1: int
  }
> But you don't need a specific concept of an interface, it's just a record of closures.

If your language doesn't have a concept like Interface to begin with, you can't implement it out of thin air. There are few languages that don't have this. C is perhaps the only mainstream example, and indeed in C (even if you had closures) you couldn't return an object from a closure that is not a specific concrete type; or void* and expect people to cast to something else.

If your language does have interfaces, it will also have a better way to define classes corresponding to those interfaces than random closures returning interface types.

> I'm not arguing ergonomics, just implementation details, you can add all the syntax sugar you want

I've mentioned this before, but I don't know of a single language where the class mechanism is actually implemented in terms of closures. Typically there are other specialized constructs at the compiler level used specifically for this.

Re: In defense of complicated programming languages

#280
post #263

Earlier quoted context omitted.

Just a quick word about polish notation: The only reason why humans seem to prefer infix notation, is because it is what's being taught in school. The first mathematical expressions most people get to see is 1 + 1 There is no reason why someone who was introduced to + 1 1 at an early age would find it less "natural" than infix. I think the only reason why infix is the dominant notation is historical: Prefix notation…

That is not necessarily true. the first tells me we are starting with 1 apple and adding another one. the second tells me that we are starting with Adding? Adding what? We never start with an action without having a object/subject in mind.

You are still starting from your own learned context. Other languages than English have different word order conventions. As a simple extreme example, in classical Latin the main verb generally is placed last in the sentence.
Post reply on HN