Skip to content

Commit

Permalink
Merge pull request #756 from polyadic/upcast
Browse files Browse the repository at this point in the history
Implement `UpCast<T>.From(...)`
  • Loading branch information
bash authored Nov 16, 2023
2 parents 208cee3 + 6ba83af commit c55ff3b
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 2 deletions.
60 changes: 60 additions & 0 deletions Funcky.Test/UpCastTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Immutable;

namespace Funcky.Test;

public sealed class UpCastTest
{
[Fact]
public void UpCastsReferenceTypeToObject()
{
const string arbitraryStringValue = "string";

Option<object> option = UpCast<object>.From(Option.Return(arbitraryStringValue));
FunctionalAssert.Some(arbitraryStringValue, option);

Either<Unit, object> either = UpCast<object>.From(Either<Unit>.Return(arbitraryStringValue));
FunctionalAssert.Right(arbitraryStringValue, either);

Result<object> result = UpCast<object>.From(Result.Return(arbitraryStringValue));
FunctionalAssert.Ok(arbitraryStringValue, result);

Lazy<object> lazy = UpCast<object>.From(Lazy.Return(arbitraryStringValue));
Assert.Equal(arbitraryStringValue, lazy.Value);
}

[Fact]
public void UpCastsValueTypeToObject()
{
const int arbitraryIntegerValue = 10;

Option<object> option = UpCast<object>.From(Option.Return(arbitraryIntegerValue));
FunctionalAssert.Some(arbitraryIntegerValue, option);

Either<Unit, object> either = UpCast<object>.From(Either<Unit>.Return(arbitraryIntegerValue));
FunctionalAssert.Right(arbitraryIntegerValue, either);

Result<object> result = UpCast<object>.From(Result.Return(arbitraryIntegerValue));
FunctionalAssert.Ok(arbitraryIntegerValue, result);

Lazy<object> lazy = UpCast<object>.From(Lazy.Return(arbitraryIntegerValue));
Assert.Equal(arbitraryIntegerValue, lazy.Value);
}

[Fact]
public void UpCastsToInterface()
{
var arbitraryImplementationValue = ImmutableArray<string>.Empty;

Option<IEnumerable<string>> option = UpCast<IEnumerable<string>>.From(Option.Return(arbitraryImplementationValue));
FunctionalAssert.Some(arbitraryImplementationValue, option);

Either<Unit, IEnumerable<string>> either = UpCast<IEnumerable<string>>.From(Either<Unit>.Return(arbitraryImplementationValue));
FunctionalAssert.Right(arbitraryImplementationValue, either);

Result<IEnumerable<string>> result = UpCast<IEnumerable<string>>.From(Result.Return(arbitraryImplementationValue));
FunctionalAssert.Ok(arbitraryImplementationValue, result);

Lazy<IEnumerable<string>> lazy = UpCast<IEnumerable<string>>.From(Lazy.Return(arbitraryImplementationValue));
Assert.Equal(arbitraryImplementationValue, lazy.Value);
}
}
6 changes: 4 additions & 2 deletions Funcky/Funcky.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
</PropertyGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants);CONTRACTS_FULL</DefineConstants>
<TargetFrameworkForPublicApiAnalyzers>net7.0</TargetFrameworkForPublicApiAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" PrivateAssets="all" Condition="'$(TargetFramework)' == 'net7.0'" />
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" PrivateAssets="all" Condition="'$(TargetFramework)' == '$(TargetFrameworkForPublicApiAnalyzers)'" />
<PackageReference Include="Microsoft.Bcl.HashCode" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
<PackageReference Include="Nullable" PrivateAssets="all" />
<PackageReference Include="IsExternalInit" PrivateAssets="all" />
Expand All @@ -33,7 +34,8 @@
<ItemGroup>
<ProjectReference Include="..\Funcky.SourceGenerator\Funcky.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<!-- These files are included by Microsoft.CodeAnalysis.PublicApiAnalyzers is included. -->
<ItemGroup Condition="'$(TargetFramework)' != '$(TargetFrameworkForPublicApiAnalyzers)'">
<AdditionalFiles Include="PublicAPI.Shipped.txt" />
<AdditionalFiles Include="PublicAPI.Unshipped.txt" />
</ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions Funcky/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ Funcky.Monads.Result<TValidResult>.GetOrElse(TValidResult fallback) -> TValidRes
Funcky.Monads.Result<TValidResult>.InspectError(System.Action<System.Exception!>! inspector) -> Funcky.Monads.Result<TValidResult>
Funcky.Monads.Result<TValidResult>.OrElse(Funcky.Monads.Result<TValidResult> fallback) -> Funcky.Monads.Result<TValidResult>
Funcky.Monads.Result<TValidResult>.OrElse(System.Func<System.Exception!, Funcky.Monads.Result<TValidResult>>! fallback) -> Funcky.Monads.Result<TValidResult>
Funcky.UpCast<TResult>
static Funcky.Extensions.EnumeratorExtensions.MoveNextOrNone<T>(this System.Collections.Generic.IEnumerator<T>! enumerator) -> Funcky.Monads.Option<T>
static Funcky.Extensions.ParseExtensions.ParseNumberOrNone<TNumber>(this string! value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option<TNumber>
static Funcky.Extensions.ParseExtensions.ParseNumberOrNone<TNumber>(this System.ReadOnlySpan<char> value, System.Globalization.NumberStyles style, System.IFormatProvider? provider) -> Funcky.Monads.Option<TNumber>
static Funcky.Extensions.ParseExtensions.ParseOrNone<TParseable>(this string? value, System.IFormatProvider? provider) -> Funcky.Monads.Option<TParseable>
static Funcky.Extensions.ParseExtensions.ParseOrNone<TParseable>(this System.ReadOnlySpan<char> value, System.IFormatProvider? provider) -> Funcky.Monads.Option<TParseable>
static Funcky.Extensions.StringExtensions.Chunk(this string! source, int size) -> System.Collections.Generic.IEnumerable<string!>!
static Funcky.Extensions.StringExtensions.SlidingWindow(this string! source, int width) -> System.Collections.Generic.IEnumerable<string!>!
static Funcky.UpCast<TResult>.From<T>(System.Lazy<T>! lazy) -> System.Lazy<TResult>!
static Funcky.UpCast<TResult>.From<TItem>(Funcky.Monads.Option<TItem> option) -> Funcky.Monads.Option<TResult>
static Funcky.UpCast<TResult>.From<TLeft, TRight>(Funcky.Monads.Either<TLeft, TRight> either) -> Funcky.Monads.Either<TLeft, TResult>
static Funcky.UpCast<TResult>.From<TValidResult>(Funcky.Monads.Result<TValidResult> result) -> Funcky.Monads.Result<TResult>
54 changes: 54 additions & 0 deletions Funcky/UpCast.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Diagnostics.CodeAnalysis;

namespace Funcky;

/// <summary>Functions to upcast monad values.</summary>
public static class UpCast<TResult>
where TResult : notnull
{
/// <summary>Upcasts an <see cref="Option{TItem}"/>'s item to <typeparamref name="TResult"/>.</summary>
/// <example>
/// Upcasting an option's item from <see cref="string"/> to <see cref="object"/>:
/// <code><![CDATA[
/// Option<object> result = UpCast<object>.From(Option.Return("hello world"));
/// ]]></code></example>
public static Option<TResult> From<TItem>(Option<TItem> option)
where TItem : TResult
=> option.Select(From);

/// <summary>Upcasts the right side of an <see cref="Either{TLeft,TRight}"/> to <typeparamref name="TResult"/>.</summary>
/// <example>
/// Upcasting an either's right value from <see cref="string"/> to <see cref="object"/>:
/// <code><![CDATA[
/// Either<Error, object> result = UpCast<object>.From(Either<Error>.Return("hello world"));
/// ]]></code></example>
public static Either<TLeft, TResult> From<TLeft, TRight>(Either<TLeft, TRight> either)
where TLeft : notnull
where TRight : TResult
=> either.Select(From);

/// <summary>Upcasts the success result of a <see cref="Result{TValidResult}"/> to <typeparamref name="TResult"/>.</summary>
/// <example>
/// Upcasting a result's success value from <see cref="string"/> to <see cref="object"/>:
/// <code><![CDATA[
/// Result<object> result = UpCast<object>.From(Result.Return("hello world"));
/// ]]></code></example>
public static Result<TResult> From<TValidResult>(Result<TValidResult> result)
where TValidResult : TResult
=> result.Select(From);

/// <summary>Upcasts the value of a <see cref="Lazy{TValidResult}"/> to <typeparamref name="TResult"/>.</summary>
/// <example>
/// Upcasting a lazy's value from <see cref="string"/> to <see cref="object"/>:
/// <code><![CDATA[
/// Lazy<object> result = UpCast<object>.From(Lazy.Return("hello world"));
/// ]]></code></example>
[UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Public parameterless constructor is only used when a Lazy is created without providing a value or func.")]
public static Lazy<TResult> From<T>(Lazy<T> lazy)
where T : TResult
=> lazy.Select(From);

private static TResult From<TValue>(TValue value)
where TValue : TResult
=> value;
}

0 comments on commit c55ff3b

Please sign in to comment.