Live data from Hacker News

Sets, types and type checking

kaleidawave.github.io

31–36 of 36 posts

Re: Sets, types and type checking

#32
post #16

> In Rust we have Option , which is equivalent to T | null No, not true! As the author correctly states earlier in the post, unions are not an exclusive-or relation. Unions are often made between disjoint types, but not always . This becomes important when T itself is nullable. Let's say T is `U | null`. `Option >` in Rust has one more inhabitant than `U | null | null` in TypeScript - `Some(None)`. Union types can ce…

Sharing my TypeScript Result type for anyone who’s crazy like me and wishes they had “if let” and doesn’t want “.value” everywhere.

You can do this:

    const user = await fetchUserDTO(123);
    // user is Result
    if(!isOk(user)) { something(user.err); return; }
    user.name; // user is narrowed to UserDTO
Source:

    export const FailSym: unique symbol = (globalThis as any).____FailSym ?? Symbol('FailSym');
    (globalThis as any).____FailSym = FailSym;
    export type Result = T | {
        [FailSym]: true,
        err: E
    }
    
    export function Ok(val: T): Result { return val; }
    export function Fail(err: E): Result { return {[FailSym]: true, err}; }
    
    export function isOk(val: Result): val is T { return !(typeof val == 'object' && val != null && FailSym in val); }
    export function isFail(val: Result): val is Result { return typeof val == 'object' && val != null && FailSym in val; }
    
    export function resultify(promise: Promise): Promise> {
        return promise.then(Ok, err => Fail(err as any));
    }

Re: Sets, types and type checking

#33
post #16

> In Rust we have Option , which is equivalent to T | null No, not true! As the author correctly states earlier in the post, unions are not an exclusive-or relation. Unions are often made between disjoint types, but not always . This becomes important when T itself is nullable. Let's say T is `U | null`. `Option >` in Rust has one more inhabitant than `U | null | null` in TypeScript - `Some(None)`. Union types can ce…

> The meaning of `T | null` and the meaning of code you write with such a type does depend on what T is at runtime.

Could you give an example of this? The only case I can imagine is a union null <=: T, T | null. This would cause unexpected behaviour, i.e. a if T = (U | null), the function orElse : T | null → T → T would always return the second argument if the first is null, but this is perfectly consistent with the fact that T itself is a union type. The idea that it should do anything different is presuming that the null assigned to T is different from the null assigned to T | null (that union types are disjoint). But this is an invalid assumption. It's still a validly typed program that obeys a consistent set of rules. It's just that there is no tagging in the union type (you don't know if a member comes from the left or right side). This is only an issue if you write code expecting the union to work like a tagged union.

Re: Sets, types and type checking

#34
post #26
post #16

> In Rust we have Option , which is equivalent to T | null No, not true! As the author correctly states earlier in the post, unions are not an exclusive-or relation. Unions are often made between disjoint types, but not always . This becomes important when T itself is nullable. Let's say T is `U | null`. `Option >` in Rust has one more inhabitant than `U | null | null` in TypeScript - `Some(None)`. Union types can ce…

Rich Hickey has a presentation titled 'Maybe Not' that talks about this exact distinction, but he actually argues the reverse (and often criticized quite wildly, even though both sides are sort of right here, I believe). He says that nullability is better [1], as refactoring a function that accepted T to T?, or a function's return type from T? to T are both backwards compatible, while the sum type variants require co…

> as refactoring a function that accepted T to T?, or a function's return type from T? to T

I've always found this to be a silly argument. In both cases, the problem can be solved by interface versioning. If you have f : T → Maybe T and you should like to change the type, just create f_v2 : Maybe T → T assign f x = just (f_v2 (just x)). The real shame is that most languages do not support interface versioning as a proper feature. There should really be some way to use a package and declare that you want to use v1/2 of the interface, then subsequently package.f would either be f or f_v2. You would eventually have to change the code that deals with f if you want to stay up to date, but realistically you should remove redundant error checking code anyway so you aren't much better off.

You could also frame it as a tooling problem. Given that the transformation between types is trivial, you should be able to write a program that automatically updates code to newer versions. It seems like this is a relatively niche issue in either direction (union types not being disjoint, vs having to change code that deals with options), but its a more solvable problem in the second case.

Re: Sets, types and type checking

#35
post #34
post #26

Earlier quoted context omitted.

Rich Hickey has a presentation titled 'Maybe Not' that talks about this exact distinction, but he actually argues the reverse (and often criticized quite wildly, even though both sides are sort of right here, I believe). He says that nullability is better [1], as refactoring a function that accepted T to T?, or a function's return type from T? to T are both backwards compatible, while the sum type variants require co…

> as refactoring a function that accepted T to T?, or a function's return type from T? to T I've always found this to be a silly argument. In both cases, the problem can be solved by interface versioning. If you have f : T → Maybe T and you should like to change the type, just create f_v2 : Maybe T → T assign f x = just (f_v2 (just x)). The real shame is that most languages do not support interface versioning as a pr…

Do you know of a language that supports interface versioning? I often wished for one (as I like writing programs by "iterative refining", where I basically copy the good parts into a completely new project), but have never heard of a language that does something like that.

Re: Sets, types and type checking

#36
post #35
post #34

Earlier quoted context omitted.

> as refactoring a function that accepted T to T?, or a function's return type from T? to T I've always found this to be a silly argument. In both cases, the problem can be solved by interface versioning. If you have f : T → Maybe T and you should like to change the type, just create f_v2 : Maybe T → T assign f x = just (f_v2 (just x)). The real shame is that most languages do not support interface versioning as a pr…

Do you know of a language that supports interface versioning? I often wished for one (as I like writing programs by "iterative refining", where I basically copy the good parts into a completely new project), but have never heard of a language that does something like that.

Sadly, I do not. You can emulate it pretty well with just putting numbers on the end of function names though. I'm always a little shocked at how the people who make programming languages seem to consistently ignore some of the most obvious features in favour of weird stuff.
Post reply on HN