funcky
funcky copied to clipboard
Should we have a `Cast<T>` function on our Monads (for the main value) like `IEnumerable<T>`?
The alternatives currently:
var maybeObject = maybeInt.Select(value => value as object),
// or
var maybeObject = maybeInt.Select<object>(value => value),
Functional.Identitycannot be used because of the differing return type.- If we could make
Option<T>co-variant, this case would be a non-issue.
I would love to have something like Enumerable.Cast.
Something I dislike about Cast however, is that there is no compile-time checks.
Edit: I just realized that my extension method isn't useful because you'd always had to specify both types :/
~~I feel like most cases you want to cast upwards, so a CastUp or Upcast method would be helpful to avoid mistakes:~~
public static Option<TResult> Upcast<TItem, TResult>(this Option<TItem> option)
where TItem : notnull, TResult
where TResult : notnull => ...
~~ImmutableArray has an extension method like this:
ImmutableArray.CastUp~~
The Upcast is only possible with the two type parameters, but you could theoretically do something like this, to do type-deduction on the Option.
class UpCast<TResult>
where TResult : notnull
{
public From<TItem>(Option<T> option)
where TItem : notnull, TResult
{
// ...
}
}
Here are three possible variants which would be internally to the Option<TItem> class, which avoids the type-deduction problems.
public readonly partial struct Option<TItem>
where TItem : notnull
{
[Pure]
public Option<TResult> Cast<TResult>()
where TResult : class
=> _hasItem
? Option.Some((TResult)(object)_item)
: Option<TResult>.None;
[Pure]
public Option<TResult> DownCast<TResult>()
where TResult : TItem
=> _hasItem
? Option.Some((TResult)_item)
: Option<TResult>.None;
[Pure]
public Option<TResult> As<TResult>()
where TResult : class
=> _hasItem
? Option.FromNullable(_item as TResult)
: Option<TResult>.None;
}
Proposal:
Only implement the As function and only on Option<TItem>
Reasons:
- This is very natural and nobody will expect an exception.
- The other monads do not have None as fallback and there is no natural semantic I could think of.
- What I see used is mostly the Idea of casting in a safe way like this.
The upcast idea looks really cool 😍
I think we might be able to extend that to Either and Result as upcasts are infallible, right?
public static class UpCast<TResult>
where TResult : notnull
{
public static Option<TResult> From<TItem>(Option<TItem> option)
where TItem : notnull, TResult
=> throw new NotImplementedException();
public static Either<TLeft, TResult> From<TLeft, TRight>(Either<TLeft, TRight> either)
where TLeft : notnull
where TRight : notnull, TResult
=> throw new NotImplementedException();
// Same thing for Result
}
Yes the UpCast cannot fail since we do type check statically.
I reviewed the implementation it's merged.
I run into problems in which a DownCast would be helpful more often, thats why I tried to find an acceptable solution.
I propose a solution for DownCast<T> which looks very similar to UpCast<T> and works for Option, Either and Result.
PR #794