Live data from Hacker News

The compiler is your best friend

blog.daniel-beskin.com

81–90 of 149 posts

Re: The compiler is your best friend

#81

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

Sometimes the "error" is more like, "this is a case that logically could happen but I'm not going to handle it, nor refactor the whole program to stop it from being expressable"

Re: The compiler is your best friend

#82

Earlier 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?

>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

#83
post #62
post #53

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

I hear you, but sometimes this is what I want.

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

#84

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

Gentoo[.org] may have been easier to custom compile dependancies vs mint, but i haven't used mint.

Re: The compiler is your best friend

#85

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

Well, bitflip errors are more of a vulnerability for longer lived values. This could effect fukushima style robots or even medical equipment. ECC implemented outside of ram would save vs triplicate but it was just a question related to the-above idea of an array access being assumed as in+bounds. Thank you.

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…

Until you have a bit flip or a silicon error. Or someone changed the floating point rounding mode.

Re: The compiler is your best friend

#87

Great 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 ).

In fact, part of the implementation (or at least ideas) were borrowed from TypeScript. Nullability in C# changes depending on the checks around the code (similar to how the type of expressions in ThpeScript can change depending on prior code).

Re: The compiler is your best friend

#88

Earlier 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?

You run equivalent or equal calculations simultaneously on N computers and take majority wins, aircraft control or distributed filesystem style.

Re: The compiler is your best friend

#89
post #42

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

> That sounds eerily similar to the "OOP is for large teams" defence

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

#90

I'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,…

You have misunderstood what it means to make invalid states unrepresentable.

    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 
Post reply on HN