Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

union types + type guards are extremely awkward with loops #4040

Closed
zpdDG4gta8XKpMCd opened this issue Jul 27, 2015 · 6 comments
Closed

union types + type guards are extremely awkward with loops #4040

zpdDG4gta8XKpMCd opened this issue Jul 27, 2015 · 6 comments
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript

Comments

@zpdDG4gta8XKpMCd
Copy link

Consider an implementation of a list and a fold function in TS1.6:

module Nothing {
    export const enum Brand {}
}
interface Nothing {
    'a brand': Nothing.Brand;
}
interface Node<a> {
    value: a;
    previous: List<a>;
}
export function hasAny<a>(values: List<a>) : values is Node<a> {
    return values != null;
}
type List<a> = Node<a> |  Nothing;
var values : List<string> = undefined;

function fold<a, r>(values: List<a>, result: r, fold: (result: r, value: a) => r) : r {
   // using union types and custom guards what the body of this function should look like?
}

Now in order to iterate through such list in JavaScript all it would take is:

function fold(values, result, fold) {
    for (var node = values; node != null; node = node.previous) {
        result = fold(result, node.value);
    }
    return result;
}

So I would expect that something like this could be easily done in TypeScript using its latest safety features like type guards and union types.

// pseudo code that I wish worked
function fold<a, r>(values: List<a>, result: r, fold: (result: r, value: a) => r) : r {
   for (var node = values; hasAny(node); node = node.previous) {
       result = fold(result, node.value);
   }
   return result;
}

However what I ended up with doesn't look nearly as simple:

// real code full of awkwardness that I managed to hack
function fold<a, r>(values: List<a>, result: r, fold: (result: r, value: a) => r): r {
    var previous = values;
    while (true) {
        var current = previous;
        if (hasAny(current)) {
            result = fold(result, current.value);
            previous = current.previous;
        } else {
            break;
        }
    }
    return result;
}

Now anyone who sees the generated JavaScript would call the cops on me.

function fold(values, result, fold) {
    var previous = values;
    while (true) {
        var current = previous;
        if (hasAny(current)) {
            result = fold(result, current.value);
            previous = current.previous;
        }
        else {
            break;
        }
    }
    return result;
}

Question: Is this how union types + custom guards + loops were designed to work together or I am missing something?

@RyanCavanaugh
Copy link
Member

It seems like we should be type guarding the third clause of a for and its body by the second clause? Is that sufficient to fix this?

@zpdDG4gta8XKpMCd
Copy link
Author

third clause is a major pain point: the right side of it should be guaded, but the left should be not:

...; node /* <-- must be unguarded */  = node/ * <-- must be guarded */.previous ...

@RyanCavanaugh
Copy link
Member

Thinking about it some more, another problem is that we're never going to apply a type guard to something that's assigned to, so we'd need #2388 first to be able to remove that restriction.

Anyway, for simplicity's sake, I would write this:

function fold<a, r>(values: List<a>, result: r, fold: (result: r, value: a) => r): r {
   for (var node = <Node<a>>values; hasAny(node); node = <Node<a>>node.previous) {
       result = fold(result, node.value);
   }
   return result;
}

@zpdDG4gta8XKpMCd
Copy link
Author

sure thing you can, it's just upsetting that unions and guards are effectively useless upon arrival, hope you guys can think something up

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Jul 27, 2015
@RyanCavanaugh
Copy link
Member

Does this work as hoped with the new flow analysis?

@zpdDG4gta8XKpMCd
Copy link
Author

yes

@mhegazy mhegazy added Fixed A PR has been merged for this issue and removed Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels Apr 26, 2016
@mhegazy mhegazy added this to the TypeScript 2.0 milestone Apr 26, 2016
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Fixed A PR has been merged for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants