• 0 Posts
  • 105 Comments
Joined 1 year ago
cake
Cake day: June 18th, 2023

help-circle


  • I definitely agree on the last point. Personally I like languages where I can get the compiler to check a lot more of my reasoning, but I still want to be able to use all the memory management techniques that people use in C.

    I remember Jonathan Blow did a fairly rambling stream of consciousness talk on his criticisms of Rust, and it was largely written off as “old man yells at clouds”, but I tried to make sense of what he was saying and eventually realised he had a lot of good points.

    I think it was this one: https://m.youtube.com/watch?v=4t1K66dMhWk


  • That’s what std::move does, and you’re right that it’s quite an ugly hack to deal with C++ legacy mistakes that C doesn’t have.

    I say move semantics to refer to the broader concept, which exists to make manual memory management safer and easier to get right. It’s also a core feature of Rust.

    Also I’m talking about parametric polymorphism, not subtype polymorphism. So I mean things like lists, queues and maps which can be specialised for the element type. That’s what I can’t imagine living without.


  • I would have said the same thing a few years ago, but after writing C++ professionally for a while I have to grudgingly admit that most of the new features are very useful for writing simpler code.

    A few are still infuriating though, and I still consider the language an abomination. It has too many awful legacy problems that can never be fixed.


  • porgamrer@programming.devtoProgrammer Humor@programming.devC++
    link
    fedilink
    arrow-up
    1
    arrow-down
    1
    ·
    5 months ago

    The only conceivable way to avoid pointers in C is by using indices into arrays, which have the exact same set of problems that pointers do because array indexing and pointer dereferencing are the same thing. If anything array indexing is slightly worse, because the index doesn’t carry a type.

    Also you’re ignoring a whole host of other problems in C. Most notably unions.

    People say that “you only need to learn pointers”, but that’s not a real thing you can do. It’s like saying it’s easy to write correct brainfuck because the language spec is so small. The exact opposite is true.


  • I’m not a fan of C++, but move semantics seem very clearly like a solution to a problem that C invented.

    Though to be honest I could live with manual memory management. What I really don’t understand is how anyone can bear to use C after rewriting the same monomorphic collection type for the 20th time.



  • Mojo’s starting point is absurdly complex. Seems very obviously doomed to me.

    Julia is a very clever design, but it still never felt that pleasant to use. I think it was held back by using llvm as a JIT, and by the single-minded focus on data science. Programming languages need to be more opportunistic than that to succeed, imo.




  • Out of the ones you listed I’d suggest Julia or Clojure. They are simple and have interactive modes you can use to experiment easily.

    Experienced programmers often undersell the value of interactive prompts because they don’t need them as much. They already have a detailed mental model of how most languages behave.

    Another thing: although Julia and Clojure are simple, they are also quite obscure and have very experimental designs. Python might be a better choice. From a beginner’s perspective it’s very similar to Julia, but it’s vastly more popular and lots of people learn it as their first language.

    Based on the languages you found, I’m guessing you were looking for something simple and elegant. I think Python fits this description too.


  • REBOL is one of my biggest blind spots in programming language familiarity. I remember there was another REBOL revival project called RED, which always boasted huge feature sets with small amounts of code, though I never got around to investigating those claims myself.

    This project seems to aim to provide strong foundations for a more performant compiler, but still lacks the most powerful REBOL features. I wonder if anyone can summarise those features? In particular, is there anything fundamental that distinguishes REBOL from Lisp, Smalltalk, Ruby, etc?



  • But I don’t see why by that logic any turing-complete language wouldn’t be in the group “portable”, including any hardware-specific assembly. Because you can always implement a translator that has well defined-behaviour?

    What matters is the practical reality. Generally, languages are not portable when they don’t have well-defined behaviour, and when this causes their implementations to differ.

    And thanks to this low standard for portability, a lot of VMs and high level languages are portable until you get to the FFI.

    e.g. is 6502 assembly now portable that C64 and NES emulators are commonplace?

    I would say yes! It’s just that portability is not the only thing required to make a VM spec useful.

    But if you lacked other options, you could theoretically build gcc for 6502 assembly once, and then use the same binary to bootstrap the gcc on lots of different platforms, specifically thanks to the proliferation of NES emulators.

    This would also only work if there is a standard NES API available in all the emulators that is rich enough to back a portable libc implementation. I have no idea about that part.






  • In my opinion dependency injection solves a problem that doesn’t need to exist, and does it by adding even more obfuscation and complexity.

    The problem is that the original gang of four design patterns had very little to say about managing effects. In old java code things like network and file IO often happen deep inside the object graph, hidden behind multiple impenetrable abstractions such that it’s impossible to run the logic without triggering the effect.

    The wrong solution is to add even more obfuscation and abstraction, so that you can inject replacement classes deep inside the object graph where the effects happen. it solves the immediate problem of implementing tests, but makes everything else worse and more confusing.

    The right solution is to surface all your effects at the top level of the call graph. The logic only generates data, and passes it back up to the top level of the program. The top level code then decides whether to feed this data into an effectful operation. Now all your code is easier to reason about, and in you can easily test the logic without triggering unwanted effects.