5 ms·
Colored functions are good. It reflects the language design on signaling what is important, and what are the properties it want the writer to pay attention. Oth
by qihqi 4mo ago
Colored functions are good. It reflects the language design on signaling what is important, and what are the properties it want the writer to pay attention.
Other examples of colored functions:
* Haskell: pure function and non-pure (IO monads) looks different.
* Rust: unsafe functions (or block) requires special markers.
- clintonc 4mo agoMy mind went to Java's checked exceptions -- not sure if anyone today believes that coloring is still a good idea.
- zokier 4mo agoThe problem with checked exceptions afaik was far more in the execution than in the idea itself. And also late 90s-early 00s was different time in general.
- vips7L 4mo agoJava just makes them hard to use. They're not fully apart of the type system and they're hard to escape when you actually want to panic. Everyone around here praises Rust's result, checked exceptions are the same idea: fn someFn() -> Result<T, E> T someFn() throws E fun someFn(): T | E // Kotlin's proposed error unions Checked exceptions actually compose a little better when you have a function that can throw multiple types: T someFn() throws E, F, G This is like a union type of E | F | G. I don't know about Rust, but most languages won't let you do that over generic types like Result<T, E | F | G>. The main problem for Java's checked exceptions is just how boilerplatey they are, especially when you can't handle something. In Java if you need to become "unchecked" or panic you need to: try { someFn(); } catch (SomeException ex) { throw new RuntimeException(ex); // dunno panic } Ideally that would just be: someFn()!!!!; // shut up compile panic if this happens
- steveklabnik 4mo agoRust makes you define an enum of E, F, and G, but also provides a conversion API so you can pass any of the three and it feels like it does, at least at the site of returning the error. It also provides an error interface so sometimes you don’t need the enum, if all the types return that interface.
- vips7L 4mo agoWouldn't you lose a little compile time safety a little by returning the interface, like catching Exception? i.e. as types you don't know about get introduced the compiler won't stop bad things from happening: catch (Exception ex) { switch (ex) { case SomeException1 se1 -> .. case SomeException2 se2 -> .. default -> throw new IllegalStateException(ex); // panic } }
- steveklabnik 4mo agoDepends on what you mean by "safety," what this is really about is open vs closed set. An interface means that there's an open set of things that could be returned, whereas an enum is a closed set. Which one is correct for you depends on your code and requirements. It's true that if you return an open set of things, you'll have to handle cases you didn't explicitly account for.
- vips7L 4mo agoI just meant knowing what errors can be happen at compile time vs unchecked errors flying about. I personally am not a fan of the compiler not stopping me when a new error type is introduced. Correctness is probably the better word.
- jonnytran 4mo agoSome Rust libraries have started to implement unioning multiple error types and handling a subset of them while propagating the rest. But as far as I know, the idea hasn't caught on. Here are the crates I know of. https://github.com/komora-io/terrors https://github.com/komora-io/terrors https://github.com/mcmah309/eros https://github.com/mcmah309/eros
- sheept 4mo agoRust unsafe functions aren't a good example of colored functions because it doesn't exhibit the main issue brought up in the article, that one color can call the other but not the other way around. In Rust, unsafe code can call safe code, and safe code can call unsafe code. Calling unsafe code in safe code requires an explicit unsafe block, but that's fairly normal and not a hack to get around function coloring. A better example could be Rust async, though unlike JavaScript, you have the option to block the thread on an async function in a sync function.
- simonask 4mo agoI don’t really see how declaring an unsafe block is materially different for the purposes of this discussion than, e.g., entering an async runtime. It is code you need to write, tradeoffs you need to weigh, invariants you need to keep.
- xigoi 4mo agoIn Rust, if you have a function containing an unsafe block, you do not need to use another unsafe block to call the function. Therefore, unsafe is not “contagious” like JavaScript’s async.
- jeroenhd 4mo ago> you do not need to use another unsafe block to call the function And in C#, you can just type `await` and call an async function from a sync function. Calling unsafe requires an unsafe block from safe functions. That's essentially the same thing as async/await in many languages (Rust does things differently, of course, but that's even worse in my opinion).
- xigoi 4mo ago> And in C#, you can just type `await` and call an async function from a sync function. Yes, but not in JavaScript.
- 4mo ago