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

Match multiple unions simultaneously #63

Open
OliverJAsh opened this issue Jul 6, 2019 · 6 comments
Open

Match multiple unions simultaneously #63

OliverJAsh opened this issue Jul 6, 2019 · 6 comments

Comments

@OliverJAsh
Copy link
Collaborator

OliverJAsh commented Jul 6, 2019

In Elm you can match against multiple types at once, by storing them in a tuple and then matching against that:

type Action = AgreeToTerms | AddFiles { files : List File }
type State = Onboarding | Form { files : List File }

myMatch : Action -> State -> State
myMatch action state = case ( action, state ) of
  ( AgreeToTerms, Onboarding ) ->
    Form { files = [] }
  ( AddFiles { files }, Form form ) ->
    Form { files = form.files }
  ( _ , _ ) ->
    state

Full example: https://github.com/rtfeldman/elm-spa-example/blob/17a3398623f9e538f14f5b0d039fd62b3beae934/src/Main.elm#L210

Currently, if we want to do the same via Unionize, the best we can do is nesting matchs:

const Action = unionize({
    AgreeToTerms: ofType<{}>(),
    AddFiles: ofType<{ files: File[] }>(),
});
type Action = UnionOf<typeof Action>;

const State = unionize({
    Onboarding: ofType<{}>(),
    Form: ofType<{ files: File[] }>(),
});
type State = UnionOf<typeof State>;

const myMatch = (action: Action) => (state: State) =>
    Action.match({
        AgreeToTerms: () =>
            State.match({
                Onboarding: () => State.Form({ files: [] }),
                default: () => state,
            })(state),
        AddFiles: ({ files }) =>
            State.match({
                Form: form => State.Form({ files: form.files.concat(files) }),
                default: () => state,
            })(state),
        default: () => state,
    })(action);

It would be amazing if we could somehow provide a syntax for matching mutliple unions simultaneously. Pseudo code:

const myMatch = match([Action, State])([
    [[['AgreeToTerms', 'Onboarding']], () =>
        State.Form({ files: [] })
    ],
    [['AddFiles', 'Form'], ({ files }, form) =>
        State.Form({ files: form.files.concat(files) })
    ],
    [['default', 'default'], (_, state) =>
        state
    ]
])

I have no idea if that would be possible with TS, though. WDYT?

@OliverJAsh
Copy link
Collaborator Author

OliverJAsh commented Jul 7, 2019

I was thinking about how you might do this. Here's what I've got so far:

// Map over a tuple to create a new tuple
type Matches<Tuples extends Array<[any, any]>, Result> = {
    [K in keyof Tuples]: [Tuples[K], (params: Tuples[K]) => Result]
};

// type Foo = 'a' | 'b'
// type Baz = 1 | 2

// From Foo and Bar we derive a tuple of pairs, to create the product of both unions.
// Unfortunately this isn't possible in TS—there's no way to convert a union to a tuple. See:
// https://github.com/microsoft/TypeScript/issues/13298
// Thus we must hardcode it.
type Tuples = [['a', 1], ['b', 1], ['a', 2], ['b', 2]];

type Result = Matches<Tuples, any>;

const result: Result = [
    [['a', 1], ([x, y]) => {}],
    [['b', 1], ([x, y]) => {}],
    [['a', 2], ([x, y]) => {}],
    [['b', 2], ([x, y]) => {}],
];

The lack of "union to tuple" seems like a showstopper, although we should add our case to that thread, because it seems like so far few people have found compelling use cases.

The next question would be: where does default fit into this?

@karol-majewski
Copy link
Contributor

karol-majewski commented Jul 8, 2019

The lack of "union to tuple" seems like a showstopper

Technically it is possible, but it's horrible for performance and it's rather a bad idea in general. Doesn't seem like the idea will get support from the TypeScript team anytime soon. 😢

@OliverJAsh
Copy link
Collaborator Author

Doesn't seem like the idea will get support from the TypeScript team anytime soon. 😢

Maybe we can change that by we chiming in with our use case (as Ryan has requested), which is quite a compelling one IMO. 🤔

@karol-majewski
Copy link
Contributor

which is quite a compelling one IMO

I agree! There weren't many successful attempts to emulate pattern matching so far, so the concept might still be fresh, but I believe the TypeScript team acknowledges the potential it brings. We only need a few instruments. Definitely worth trying at least.

@OliverJAsh
Copy link
Collaborator Author

@gillchristian is working on something similar housinganywhere/match#2 😄

@ghost
Copy link

ghost commented Mar 5, 2020

I think, from dev team perspective the use cases are rather 1) consistent IDE type display of union types 2) consistent output in .d.ts files, see microsoft/TypeScript#17944 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants