Live data from Hacker News

In defense of complicated programming languages

viralinstruction.com

281–290 of 379 posts

Re: In defense of complicated programming languages

#281
post #269

Earlier quoted context omitted.

Make Dog implement Healable, duh.

The original author can do that. Another author cannot add that functionality later, as a library. The can only define a new kind of dog.

Subclass Dog into MyDog, which is exactly the same as Dog. From there, use and modify MyDog as though you would be modifying Dog. Any class that references base class Dog isn't modifiable, but any future work you can use MyDog instances to get all the functionality you need (with some ugly casting). There are other solutions as well.

Re: In defense of complicated programming languages

#282

Earlier quoted context omitted.

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 co…

Yeah your first approach to runtime dispatch is just dumb (don't listen to those silly people), you want the second in all cases, even with a closure implementation of classes. Objects should be implemented as in the second case, or more concretely, as:

    struct MyObj {
      vtable: MyInterface*
      data: void*
    }
Which is how dynamic traits are implemented in Rust. The C++ approach of a pointer to:

    struct Obj {
      vtable: ...
      field1: T1
      field2: T2
      field3: T3
    }
Pretty much means you need to define all the interfaces you implemente on class declaration, which is not nearly as nice as doing it independently (as with traits, protocols, typeclasses, go interfaces, etc.)

My point is that MyInterface is just a vtable, a class is a function that fills in that vtable, an object is a pair of a filled in vtable and data. These mechanism are restricted to the compiler for no good reason, which is why you end up with "metaclasses" and "mixins" and all that other nonsense. Just expose the mechanism itself and give it some nice syntax.

Classes mix too much stuff together: inheritance, subtyping, access control, data specifications, interface specifications, etc. We've figured out that's not really a good thing which is why Go and Rust rightfully split them up and use separate mechanisms.

I'm saying go even further and don't hide the runtime representation, let me create these things at runtime if I so wish. That gives metaclasses and mixins and what not for free (with a constexpr-like mechanism they can even be done at compile time).

You can still keep traits as the way of defining bundles of methods, they're also useful as generic concepts, that goes into the ergonomics of it.

This is a similar discussion to Zig generics. Zig has no idea what generics are, a "generic" is just a function that given a type produces another type. Works pretty well.

> A class is any construct that can:

That's just your definition. Your points describe an abstract datatype, a concept that predates classes. We could call that a class these days, but that's not reflective of the programming language construct called "class" as used in language like C++, Java, C# and friends.

Re: In defense of complicated programming languages

#283
post #23

Earlier quoted context omitted.

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.

Some would say that starting from lambda calculus it would be enough to reach there.

I advise some reading "The Art of the Metaobject Protocol", or similar literature.

Re: In defense of complicated programming languages

#284
post #283

Earlier quoted context omitted.

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.

Some would say that starting from lambda calculus it would be enough to reach there. I advise some reading "The Art of the Metaobject Protocol", or similar literature.

Obviously any Turing complete language can implement an object system, but that's clearly different from the language having an object system built in.

Re: In defense of complicated programming languages

#285

Earlier quoted context omitted.

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.

But then you'll have a branch on every failable operation and slow down the happy path. This is not too different from passing the error as a value.

Instead, compilers use long jumps and manually edit the stack. I'm not sure it makes a lot of difference today, but branches were really problematic by the time the OOP languages were popularizing.

Re: In defense of complicated programming languages

#286
post #260

Earlier quoted context omitted.

> the compiler doesn't let me have two mutable references to that struct even if one of them only changes X and the other one Y. The semantics of mutable references and other interface constructs must be independent from actual implementation, to allow for changes in the latter. So Rust is behaving as expected here. If the changes to X and Y are truly independent, you can have a function that returns separate mutable…

> then the changes are no longer truly independent. But the author of the code is telling you that they are independent. The author may be proved wrong if some future change accidentally triggers dependence but we as humans can have knowledge that the compiler can not have. The compiler sees a possibility of conflict and wants to protect you from yourself. That is a usability issue.

Rust is exploring techniques to allow "the author of the code" to provide finer "separation" semantics without resorting to unsafe - this is what GhostCell, QCell, LCell etc. are all about though their "usability" is still low. There's plenty of ongoing research in this space, so feel free to stay tuned for more!

Re: In defense of complicated programming languages

#287

Earlier quoted context omitted.

> A language with a short learning curve is like a toolbox that’s nearly empty. You quickly run out of ways it could help you. Very quotable! That's why I like it when languages allow you to start writing simple code and gradually make it more complex. Haskell for example is not like that. Python does better here. I think Scala is one of the best languages in that regard.

> Haskell for example is not like that. Python does better here. I think Scala is one of the best languages in that regard. This seems ripe for getting a lot of passionate anecdotes out of the woodwork.

Ha or not. Looks like I was wrong.

Re: In defense of complicated programming languages

#288

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…

White space is bad for readability anyways. I don't think that would be doable.

Ancient texts always seem to have no word breaks, they're just a solid wall of letters.

Re: In defense of complicated programming languages

#289

Earlier quoted context omitted.

> unfamiliar gets conflated with unintuitive and hard all the time In a deep sense unfamiliar and unintuitive/hard can be viewed as the same thing (see e.g. the invariance theorem for Kolgomorov Complexity). Hence striving to be "familiar" is still a virtue that it makes sense for a programming language to aspire to (balanced of course against other concerns).

Familiarity is a characteristic of the agent. Intuitiveness/duficult is of the object.

Objects are not intrinsically difficult or intuitive though. Different people will perceive the same thing to have different levels of difficulty or intuitiveness.

Or as Von Neumann said about mathematics (and I think the same is applicable to CS and programming): "You don't understand things. You just get used to them."

Re: In defense of complicated programming languages

#290

Earlier quoted context omitted.

I understand this thought process, but in my opinion it's the wrong way to think about software concepts. Understanding what a bridge is doesn't mean knowing how to build one, and in fact tying your understanding to a certain implementation of a bridge just limits your ideas about what is, in fact, an abstract concept. We understood functions as "mappings" between objects for hundreds of years, and when programming c…

I didn't understand how engines worked until I took them apart, either. I was taking things apart to understand them long before computers :-) But the notions of sending a "message" to a "method" just was way way too handwavy for me. I like examining the theory after I learn the nuts and bolts. > Understanding what a bridge is doesn't mean knowing how to build one If you don't know how to build one, you don't underst…

> But the notions of sending a "message" to a "method"

You mean to an “object”?

Also, this looks like a perfect example of theory vs practice, having different implementations of object communication as in classic OOP vs actors etc?

Post reply on HN