> How many times did you leave a comment on some branch of code stating "this CANNOT happen" and thrown an exception? Did you ever find yourself surprised when eventually it did happen? I know I did, since then I at least add some logs even if I think I'm sure that it really cannot happen. I'm not sure what the author expects the program to do when there's an internal logic error that has no known cause and no defini…
Ideally, if you can convince yourself something cannot happen, you can also convince the compiler, and get rid of the branch entirely by expressing the predicate as part of the type (or a function on the type, etc.) Language support for that varies. Rust is great, but not perfect. Typescript is surprisingly good in many cases. Enums and algebraic type systems are your friend. It'll never be 100% but it sure helps fil…
The compiler is your best friend
81–90 of 149 posts
Re: The compiler is your best friend
#82Earlier quoted context omitted.
If such an error happens, that would be a compiler bug. Why? Because I usually do checks against the length of the array or have it done as part of the standard functions like `map`. I don't write such assumptions unless I'm really sure about the statements, and even then I don't.
How does one defend against cosmic rays? Keep two copies or three like RAID? Edit: ECC ram helps for sure, but what else?
Unless you are in the extremely small minority of people who would actually be affected by it (in which case your company would already have bought ECC ram and made you work with three isolated processes that need to agree to proceed): you don't. You eat shit, crash and restart.
Re: The compiler is your best friend
#83Earlier quoted context omitted.
It really depends on the language you use. Personally I like the way rust does this: - assert!() (always checked), - debug_assert!() (only run in debug builds) - unreachable!() (panics) - unsafe unreachable_unchecked() (tells the compiler it can optimise assuming this is actually unreachable) - if cfg!(debug_assertions) { … } (Turns into if(0){…} in release mode. There’s also a macro variant if you need debug code to…
> debug_assert!() (only run in debug builds) debug_assert!() (and it's equivalent in other languages, like C's assert with NDEBUG) is cursed. It states that you believe something to be true, but will take no automatic action if it is false; so you must implement the fallback behavior if your assumption is false manually (even if that fallback is just fallthrough). But you can't /test/ that fallback behavior in debug…
For example, I’m pretty sure some complex invariant holds. Checking it is expensive, and I don’t want to actually check the invariant every time this function runs in the final build. However, if that invariant were false, I’d certainly like to know that when I run my unit tests.
Using debug_assert is a way to do this. It also communicates to anyone reading the code what the invariants are.
If all I had was assert(), there’s a bunch of assertions I’d leave out of my code because they’re too expensive. debug_assert lets me put them in without paying the cost.
And yes, you should run unit tests in release mode too.
Re: The compiler is your best friend
#84So his view is from a programmer / developer. That's fine. I had an issue on my local computer system yesterday; manjaro would not boot with a new kernel I compiled from source. It would freeze, at the boot menu, which I never had before. Anyway. I installed linuxmint today and went on to actually compile a multitude of things from source. I finally finished compiling mesa, xorg-server, ffmpeg, mpv, gtk3 + gtk4 - and…
Re: The compiler is your best friend
#85Earlier quoted context omitted.
How does one defend against cosmic rays? Keep two copies or three like RAID? Edit: ECC ram helps for sure, but what else?
>How does one defend against cosmic rays? Unless you are in the extremely small minority of people who would actually be affected by it (in which case your company would already have bought ECC ram and made you work with three isolated processes that need to agree to proceed): you don't. You eat shit, crash and restart.
Re: The compiler is your best friend
#86> How many times did you leave a comment on some branch of code stating "this CANNOT happen" and thrown an exception? Did you ever find yourself surprised when eventually it did happen? I know I did, since then I at least add some logs even if I think I'm sure that it really cannot happen. I'm not sure what the author expects the program to do when there's an internal logic error that has no known cause and no defini…
Ideally, if you can convince yourself something cannot happen, you can also convince the compiler, and get rid of the branch entirely by expressing the predicate as part of the type (or a function on the type, etc.) Language support for that varies. Rust is great, but not perfect. Typescript is surprisingly good in many cases. Enums and algebraic type systems are your friend. It'll never be 100% but it sure helps fil…
Re: The compiler is your best friend
#87Great read. C# has the concept of nullable reference types[1] which requires you to be explicit if a variable can be null and the compiler is aware of this. I would love to see a similar feature in languages like TypeScript and Go. [1]: https://learn.microsoft.com/en-us/dotnet/csharp/nullable-ref...
TypeScript has disctinct nullable and non-null types if you enable `strictNullChecks` or `strict` in TSConfig ( https://www.typescriptlang.org/tsconfig/#strictNullChecks ).
Re: The compiler is your best friend
#88Earlier quoted context omitted.
If such an error happens, that would be a compiler bug. Why? Because I usually do checks against the length of the array or have it done as part of the standard functions like `map`. I don't write such assumptions unless I'm really sure about the statements, and even then I don't.
How does one defend against cosmic rays? Keep two copies or three like RAID? Edit: ECC ram helps for sure, but what else?
Re: The compiler is your best friend
#89Earlier quoted context omitted.
The kind of noun-based programming you don’t like is great for large teams and large code bases where there is an inherent communication barrier based on the number of people involved. (N choose 2 = N*(N-1)/2 so it grows quadratically.) Type hierarchies need to be the primary interface between the programmers and the code because it communicates invariants on the data more precisely than words. It is dogmatic, becaus…
That sounds eerily similar to the "OOP is for large teams" defence which is simply not true. On the contrary, this noun-based programming explodes with complexity on large teams. Yes, interfaces are obviously important, but when every single thing is its own type and you try to solve problems with the type system leading to a combinatoric explosion of types and their interactions, what do you think happens when you s…
False. They are only similar to you. Haskell is a pure functional programming language and it is very much noun-based. Type classes like functors and monads are nouns that describe the structure of many types. Modern Haskell best practices involve way more types than other languages. Very few people operate on JSON for example, instead almost everyone will parse that JSON into a domain-specific type. The “parse don’t validate” idea is based on the idea that data that has been checked and data that has not been checked should have different types.
Rust also is decidedly not OOP: it does not even have inheritance. Yet it also has way more types than usual. Most languages would be satisfied with something like a Hashable interface, but Rust further decouples the calculation of hash values into traversing a type's fields and updating the internal state of the hash function. This results in both Hash and Hasher types. This is a wonderful design decision that helps programmers despite an increase in the number of nouns.
> a combinatoric explosion of types and their interactions
Absolutely not my experience at all. There is nothing combinatoric here. Most types do not interact with many other types. The structure is more like a tree than a complete graph.
Re: The compiler is your best friend
#90I'm not a fan of the recent trend in software development, started by the OOP craze but in the modern day largely driven by Rust advocates, of noun-based programming, where type hierarchies are the primary interface between the programmer and the code, rather than the data or the instructions. It's just so... dogmatic. Inexpressive. It ultimately feels to me like a barrier between intention and reality, another abstr…
Agreed. It's often accompanied by the dogma "make invalid states unrepresentable" which sounds good until you start trying to encode into the type system foo.bar being 1-42 unless foo.baz is above 10, where now foo.bar can be -42-1 instead, but if foo.omfg is prefixed with "wtf" then foo.baz needs to be above 20 for its modifiers to kick in. Yeah good luck doing that in the type system in a way that is maintainable,…
data UnvalidatedFoo = UnvalidatedFoo
{ unvalidatedOmfg :: String,
unvalidatedBar, unvalidatedBaz :: Int
}
data ValidatedFoo = ValidatedFoo
{ validatedOmfg :: String,
validatedBar, validatedBaz :: Int
}
validate :: UnvalidatedFoo -> Maybe ValidatedFoo
validate UnvalidatedFoo {..} = do
when ("wtf" `isPrefixOf` unvalidatedOmfg) $ do
guard (unvalidatedBaz > 20)
if unvalidatedBaz > 10
then guard (unvalidatedBar >= 1 && unvalidatedBar = -42 && unvalidatedBar