• 0 Posts
  • 26 Comments
Joined 1Y ago
cake
Cake day: Aug 02, 2023

help-circle
rss

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].


Yeah, projects also exist in the real world and practical considerations matter.

The legacy C/C++ code base might slowly and strategically have components refactored into rust, or you might leave it.

The C/C++ team might be interested in trying Rust, but have to code urgent projects in C/C++.

In the same way that if you have a perfectly good felling axe and someone just invented the chain saw, you’re better off felling that tree with your axe than going into town, buying a chainsaw and figuring out how to use it. The axe isn’t really the right tool for the job anymore, but it still works.


C is not how a computer truly works.

If you want to know how computers work, learn assembly and circuit design. You can learn C without ever thinking about registers, register allocation, the program counter, etc.

Although you can learn assembly without ever learning about e.g. branch prediction. There’s tons of levels of abstraction in computers, and many of the lower level ones try to pretend you’ve still got a computer from the 80s even though CPUs are a lot more complex than they used to be.

As an aside, I’ve anecdotally heard of some schools teaching Rust instead of C as a systems language in courses. Rust has a different model than C, but will still teach you about static memory vs the stack vs the heap, pointers, etc.

Honestly, if I had to write some systems software, I’d be way more confident in any Rust code I wrote than C/C++ code. Nasal demons scare me.


Right tool for the job, sure, but that evolves over time.

Like, years back carpenters didn’t have access to table saws that didn’t have safety features that prevent you from cutting off your fingers by stopping the blade as soon as it touches them. Now we do. Are old table saws still the “right tool for the job”, or are they just a dangerous version of a modern tool that results in needless accidents?

Is C still the right tool for the job in places where Rust is a good option?


Pure functions should be referentially transparent; you should be able to replace them with whatever value they evaluate to without changing the semantics of your code.

Throwing is referentially impure: what value do you get from calling x => throw new RuntimeException()?

Instead, functional languages prefer to return a tagged union of the value or the error.


Functional languages typically have type inference, so the type signatures are entirely optional. I haven’t looked that deeply at unison, but I’d be entirely unsurprised if it had global type inference and if all or most type signatures were optimal.

It’s less that you have to declare something can do IO or throw an exception, and more that you’re calling something from the standard library that does IO or throws an exception.

Most stuff does neither. There’s a type level distinction between normal, regular pure code, and impure effectful code, so it’s easy to tell from the type signature whether a function is pure or not.


What are some places that don’t look like Linux but actually are?

I’d think most PC games and other desktop GUI software runs on windows and looks like it runs on windows. And I’d imagine that a web browser on windows isn’t secretly a Linux environment.

But yeah, I’ve written way more code that runs on a web browser or a Linux server than runs on windows.


MicroSD cards are better, here. They’re 250mg; a pigeon can transport 75g. That’s 300 microSD cards, ignoring the weight of the SD card enclosure.


It’s a real quote, from the 80s, published in a networking textbook.

It’s amusing, but it’s always been a serious and occasionally practical observation.


IPoAC is a joke about printing actual IP packets, sending them by pigeon, then scanning them.

You do the whole usual TCP ACK/SYN thing, but with pigeons.

It’s not the same as ‘sneakernet, but strapping microsd cards to a pigeon’. It’s way, way sillier.


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.


SCIP is great and completely worth reading, but it’s not really a data structures book, and while it has various algorithms in it, it’s not really that similar to an algorithms book either.

It was intended more as a CS 101 book than as either an algorithms course or a data structure course.


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.


I’ve never actually used PHP.

Does it still have random Hebrew in error messages like ‘unexpected T_PAAMAYIM_NEKUDOTAYIM’?


The old joke is that C++ is an octopus made by nailing legs to a dog.

So it should probably be a rifle-chaku made by connecting two Garands with a chain.

C# vs Java is also really weird since C# started out as basically a Java clone.


Would you really rather see <\Foo> than )?

There’s a reason why most popular languages use } rather than end if or fi. The added verbosity doesn’t actually help people read your code more than e.g. indentation or editors with paren matching or rainbow parens.


Python virtual environments feel really archaic. It’s by far the worst user experience I’ve had with any kind of modern build system.

Even a decade ago in Haskell, you only had to type cabal sandbox init only once, rather than source virtualenv/bin/activate.sh every time you cd to the project dir.

I’m not really a python guy, but having to start touching a python project at work was a really unpleasant surprise.


The problem in FPTP is that it works really, really badly when you’ve got 3 or more viable candidates in one election.

As an activist in a FPTP system, you can either try to make a successful third party, or co-opt one of the existing ones during candidate selection. Both are very difficult, but the second approach is generally much easier, because you don’t have to deal with vote splitting.


But maybe the conservative strategists would see that they are courting a smaller fringe than if they had courted the socially progressive.

That would only really work if Liberals and NDP splitting the socially progressive vote doesn’t cause them to consistently lose.

What’s the stable equilibrium of everyone voting honestly? Each party moves to get about a third of the votes? You could reliably have an election where 2/3rds of the electorate would prefer anyone but the conservative, yet the conservative wins?

FPTP is a garbage tier electoral system.


That’s actually the inverse of what ranked choice does.

Ranked choice fulfills “later-no-harm”; filling out a third choice can never hurt your second or first choice.

Because of that, it fails “favorite betrayal”; there are times when you get a worse outcome by voting for your honest favorite.

That’s mostly because ranked choice doesn’t consider your second or third picks until your first and second have been eliminated. So there’s a bunch of weird edgecases where a compromise candidate with enough second, third etc. votes to win in the final round gets eliminated early on before they actually get any second, third etc. place support.

Suppose there’s an election like that where the Liberal is the compromise candidate that could beat either the NDP or Conservative candidate in the final round, but because the NDP and Conservative get more first-place votes, the election goes Conservative. Depending on the particulars, NDP voters could potentially have elected the Liberal by staying home, or even by voting Conservative. Either way, they’d have been better off strategically voting for the Liberal than voting honestly for the NDP.

In general, voting honestly in ranked choice is only safe either if you’re voting for a fringe third party that could never win or if you’re voting for one of the two candidates with the most total popularity.


“Getting more votes” doesn’t help in FPTP unless you actually get a plurality of the votes.

If everyone voted honestly, the biggest effect of the NDP would be to help the conservatives win more elections.