• 0 Posts
  • 38 Comments
Joined 1 year ago
cake
Cake day: August 2nd, 2023

help-circle
  • And memory bugs are only a subset of bugs that can be exploited in a program. Pretending Rust means no more exploitation is stupid.

    This is facile.

    According to Microsoft, about 70% of security bugs they see are memory safety issues.

    Yes: if you introduce memory safety, there’s still those 30% of security bugs left. But, well, I’d rather worry about 30% of issues than 100%…

    Similarly, I use libraries that eliminate SQL injections unless you really go out of your way.


  • Symbols display with friendly string-y names in a number of languages. Clojure, for example, has a symbol type.

    And a number of languages display friendly strings for enumy things - Scala, Haskell, and Rust spring to mind.

    The problem with strings over enums with a nice debugging display is that the string type is too wide. Strings don’t tell you what values are valid, strings don’t catch typos at compile time, and they’re murder when refactoring.

    Clojure symbols are good at differentiation between symbolly things and strings, though they don’t catch typos.

    The other problem the article mentions is strings over a proper struct/adt/class hierarchy is that strings don’t really have any structure to them. Concatenating strings is brittle compared to building up an AST then rendering it at the end.

    Edit: autocorrect messed a few things up I didn’t catch.


  • Javascript is generally considered OOP, but classes weren’t widely available till 2017.

    Inheritance isn’t fundamental to OOP, and neither are interfaces. You can have a duck- typed OOP language without inheritance, although I don’t know of any off the top of my head.

    Honestly, the more fundamental thing about OOP is that it’s a programming style built around objects. Sometimes OO languages are class based, or duck typing based, etc. But you’ll always have your data carrying around it’s behavior at runtime.


  • keeping state (data) and behavior (functions) that operate on that state, together

    Importantly, that’s “together at runtime”, not in terms of code organization. One of the important things about an object is that it has dynamic dispatch. Your object is a pointer both to the data itself and to the implementation that works on that data.

    There’s a similar idea that’s a bit different that you see in Haskell, Scala, and Rust - what Haskell calls type classes. Rust gives it a veneer of OO syntax, but the semantics themselves are interestingly different.

    In particular, the key of type classes is keeping data and behavior separate. The language itself is responsible for automagically passing in the behavior.

    So in Scala, you could do something like

    def sum[A](values: List[A])(implicit numDict: Num[A]) = values.fold(numDict.+)(numDict.zero)
    

    Or

    def sum[A: Num](values: List[A]) = values.fold(_ + _)(zero)
    

    Given a Num typeclass that encapsulates numeric operations. There’s a few important differences:

    1. All of the items of that list have to be the same type of number - they’re all Ints or all Doubles or something

    2. It’s a list of primitive numbers and the implementation is kept separate - no need for boxing and unboxing.

    3. Even if that list is empty, you still have access to the implementation, so you can return a type-appropriate zero value

    4. Generic types can conditionally implement a typeclass. For example, you can make an Eq instance for List[A] if A has an Eq instance. So you can compare List[Int] for equality, but not List[Int => Int].





  • Pipoca@lemmy.worldtoProgramming@programming.dev*Permanently Deleted*
    link
    fedilink
    arrow-up
    10
    arrow-down
    1
    ·
    10 months ago

    C is many things, but elegant really isn’t one of them.

    C has always been part of the “worse is better”/New Jersey school of thinking. The ultimate goal is simplicity. Particularly simplicity of language implementation, even if that makes programs written in that language more complex or error prone. It’s historically been a very successful approach.

    Rust, on the other hand, is part of “The Right Thing”/MIT approach. Simplicity is good, but it’s more important to be correct and complete even if it complicates things a bit.

    I don’t really think of void* and ubiquitous nulls, for example, as the hallmark of elegance, but as pretty simple, kludgey solutions.

    Rust, on the other hand, brings a lot of really elegant solutions from ML- family languages to a systems language. So you get algebraic data types, pattern matching, non-nullable references by default, closures, typeclasses, expression-oriented syntax, etc.


  • Just like walking doesn’t really compete, like at all, with flying in an aircraft, Functional and Object Oriented Programming are at their best when you use whichever approach makes sense for a given situation and in any reasonably complex software that means your code should be full of both.

    I’m not really sure sure that’s true.

    In FP languages like Haskell, you get tools like algebraic data types, typeclasses, and pattern matching.

    FP is really opposed to imperative programming, while objects are opposed to algebraic data types.

    You can write OO code that’s 100% fully functional, and you can write code in Haskell or rust where you barely notice you never once used an object.








  • What happens after you merge a feature branch into main and delete it? What happens to the branch?

    Afterwords, what git commands can you run to see what commits were made as part of the feature branch and which were previously on main?

    Mercurial bookmarks correspond to git branches, while mercurial tags correspond to git tags.


  • One problem, I think, is that git names are kinda bad. A git branch is just a pointer to a commit, it really doesn’t correspond to what we’d naturally think of as a branch in the context of a physical tree or even in a graph.

    That’s a bit problematic for explaining git to programming newbies, because grokking pointers is famously one of the stumbling blocks people have, along with recursion. Front-end web developers who never learned C might not really grok pointers due to never really having to deal with them much.

    Some other version control systems like mercurial have both a branch in a more intuitive sense (commits have a branch as a bit of metadata), as well as pointers to commits (mercurial, for example, calls them bookmarks).

    As an aside, there’s a few version control systems like darcs where instead of the first-class concept being snapshots, it’s diffs. There’s no separate cherrypick command in darcs, it’s just one way you can use the regular commands.



  • There are really very few OO languages that really lean FP.

    The essence of FP is working with pure functions that simply map their input to their outputs without mutating anything.

    To do that, you need immutability by default, or at least easy immutability. Very few OO languages do that in their standard libraries. Scala comes to mind as one of the few exceptions. There’s also a bunch of FP languages that bolted an object system into them, like Ocaml and F#.

    There’s a lot of OO languages that take some features like closures from FP languages, but it’s typically a royal pain to actually do anything functionally in those languages. Java and python come to mind here. Javascript used to be a huge pain, but with spread syntax it’s gone down to only moderately painful.

    And while you can definitely do FP + OO, to be honest there’s a bunch of similar but distinct language features in FP languages that I kinda prefer instead.


  • And where does your state go? How would a list of products be represented in FP?

    The essence of FP is functions working on immutable data.

    In FP, you have an immutable list of immutable products. This is basically the same as how when processing strings, you typically have an immutable array of immutable characters. If you concatenate two strings, you get a new immutable string. If you edit a string, you just get a new string. You don’t just edit the array itself directly.

    Because everything is immutable, though, you can be smarter about updates. All copies can be shallow. Adding something to the front of a linked list is just a matter of making a new node that points to the old list. Arrays are harder, but you can use some clever tree-based structures like a Hash Array Mapped Trie to get fast updates.

    So your state goes into the arguments of your functions.