DavidArno / SuccincT

Discriminated unions, pattern matching and partial applications for C#
MIT License
266 stars 15 forks source link

Would be nice to have a CaseOfType<T> override on unions #5

Closed shekelator closed 8 years ago

shekelator commented 8 years ago

The syntax of matching Case1, Case2, etc., could be more readable. It would be nice if there were a way to make it clear in the match which type was which. So instead of:

return result.Match<IHttpActionResult>()
    .Case1().Do(Ok)
    .Case2().Do(InternalServerError())
    .Result();

it might allow something like:

return result.Match<IHttpActionResult>()
    .CaseOfType<ResultObject().Do(Ok)
    .CaseOfType<Error>().Do(InternalServerError())
    .Result();

I'm working on a PR for this as well, but thought I would at least create the issue first, in case you think it's a terrible idea.

DavidArno commented 8 years ago

I really like this idea. The only small change I'd suggest is to use CaseOf, rather than CaseOfType, but if you are happy to code it and submit a PR, I'll go with your solution.

shekelator commented 8 years ago

Ok, I'm working on it. A bit stuck at the moment, so I won't be offended if anyone else wants to implement it. But will be circling back to try to make it work shortly.

DavidArno commented 8 years ago

@shekelator,

Quick sanity check here. I have tried adding CaseOf<T> to Union<T1,T2>. However, as it's not possible to use OR to constrain a generic type, I can't do something like:

public UnionPatternCaseHandler<UnionOfTwoPatternMatcher<T1, T2>, T> CaseOf<T>()
    where T : T1|T2 ...

The best I can do is:

public UnionPatternCaseHandler<UnionOfTwoPatternMatcher<T1, T2>, T> CaseOf<T>()
{
    if (typeof(T) == typeof(T1))
    {
        return new UnionPatternCaseHandler<UnionOfTwoPatternMatcher<T1, T2>, T1>(RecordAction, this)
            as UnionPatternCaseHandler<UnionOfTwoPatternMatcher<T1, T2>, T>;
    }

    if (typeof(T) == typeof(T2))
    {
        return new UnionPatternCaseHandler<UnionOfTwoPatternMatcher<T1, T2>, T2>(RecordAction, this)
            as UnionPatternCaseHandler<UnionOfTwoPatternMatcher<T1, T2>, T>;
    }

    throw new InvalidCaseOfTypeException(typeof(T));
}

This means the following would be supported:

public static string ElseExample(Union<int, bool> value) => 
    value.Match<string>()
            .CaseOf<int>().Of(0).Do("0")
            .CaseOf<bool>().Of(false).Do("false")
            .Else("??")
            .Result();

and that'll work fine. However, eg CaseOf<string>()... could be used in the above expression and cannot be checked for at compile-time. Instead, an InvalidCaseOfTypeException would be thrown at runtime.

Does that found worth doing? If so, I'll add it as a new feature to the Union classes for the next release (likely 1-2 weeks away).

shekelator commented 8 years ago

To me that definitely sounds worth doing. I tried something similar, but was stuck on the same issue. Many thanks! Sorry I wasn't able to contribute it myself.

DavidArno commented 8 years ago

This feature has been implemented in 1.6.0, which is now released.