/// <summary> /// (<$) /// <para>a = TResult, b = TSource</para> /// <para>Create a new f a from an f b by replacing all of the values in /// the f b by a given value of type a.</para> /// </summary> public static Mayhap <TResult> ReplaceWith <TSource, TResult>( TResult value, Mayhap <TSource> mayhap) where TResult : notnull { // (<$) :: Functor f => a -> f b -> f a | infixl 4 | // // (<$) :: a -> f b -> f a // (<$) = fmap . const // // Replace all locations in the input with the same value. The // default definition is fmap . const, but this may be overridden // with a more efficient version. // // Examples: // "xxx" <$ Nothing == Nothing // "xxx" <$ Just 1 == Just "xxx" #if STRICT_HASKELL return(Map(__const, mayhap)); // NB: this is just (_ => value). TResult __const(TSource x) => Thunks <TResult, TSource> .Const1(value, x); #else return(mayhap.Select(_ => value)); #endif }
public static Mayhap <TResult> Join <T, TInner, TKey, TResult>( this Mayhap <T> @this, Mayhap <TInner> inner, Func <T, TKey> outerKeySelector, Func <TInner, TKey> innerKeySelector, Func <T, TInner, TResult> resultSelector) { Utilities.Guard.NotNull(outerKeySelector, nameof(outerKeySelector)); Utilities.Guard.NotNull(innerKeySelector, nameof(innerKeySelector)); Utilities.Guard.NotNull(resultSelector, nameof(resultSelector)); var comparer = EqualityComparer <TKey> .Default; var keyLookup = __getKeyLookup(); return(@this.SelectMany(__valueSelector, resultSelector)); Mayhap <TInner> __valueSelector(T outer) => keyLookup(outerKeySelector(outer)); Func <TKey, Mayhap <TInner> > __getKeyLookup() { return(outerKey => inner.Select(innerKeySelector) .Where(innerKey => comparer.Equals(innerKey, outerKey)) .ContinueWith(inner)); } }
/// <summary> /// pure /// <para>Embed pure expressions, ie lift a value.</para> /// </summary> public static Mayhap <T> Pure <T>(T value) { #if STRICT_HASKELL throw new NotImplementedException("Applicative pure"); #else return(Mayhap <T> .η(value)); #endif }
// First law: the identity map is a fixed point for Select. // fmap id == id public static bool IdentityRule <T>(Mayhap <T> mayhap) { #if STRICT_HASKELL return(Map(Thunks <T> .Ident, mayhap) == Thunks <Mayhap <T> > .Ident(mayhap)); #else return(mayhap.Select(Thunks <T> .Ident) == Thunks <Mayhap <T> > .Ident(mayhap)); #endif }
// Second law: Select preserves the composition operator. // fmap (f . g) == fmap f . fmap g public static bool CompositionRule <T1, T2, T3>( Mayhap <T1> mayhap, Func <T2, T3> f, Func <T1, T2> g) { #if STRICT_HASKELL return(Map(x => f(g(x)), mayhap) == Map(f, Map(g, mayhap))); #else return(mayhap.Select(x => f(g(x))) == mayhap.Select(g).Select(f)); #endif }
public static Mayhap <TResult> SelectMany <T, TMiddle, TResult>( this Mayhap <T> @this, Func <T, Mayhap <TMiddle> > selector, Func <T, TMiddle, TResult> resultSelector) { Utilities.Guard.NotNull(selector, nameof(selector)); Utilities.Guard.NotNull(resultSelector, nameof(resultSelector)); return(@this.Bind( x => selector(x).Select( middle => resultSelector(x, middle)))); }
/// <summary> /// (<*) /// <para>Sequence actions, discarding the value of the second argument. /// </para> /// </summary> public static Mayhap <TSource> Ignore <TSource, TOther>( this Mayhap <TSource> @this, Mayhap <TOther> other) { // (<*) :: f a -> f b -> f a // (<*) = liftA2 const #if STRICT_HASKELL return(Lift(Thunks <TSource, TOther> .Const1).Invoke(@this, other)); #else return(@this); #endif }
public static Mayhap <Mayhap <T> > Optional <T>(this Mayhap <T> @this) { // optional :: Alternative f => f a -> f (Maybe a) // optional v = Just <$> v <|> pure Nothing // // One or none. #if STRICT_HASKELL return(Map(Mayhap <T> .Some, @this).Otherwise(Pure(Mayhap <T> .None))); #else return(@this.Select(Mayhap <T> .Some).OrElse(Pure(Mayhap <T> .None))); #endif }
/// <summary>many</summary> public static Mayhap <IEnumerable <T> > Many <T>(Mayhap <T> mayhap) { // many :: f a -> f [a] // many v = many_v // where // many_v = some_v <|> pure [] // some_v = liftA2 (:) v many_v // // many v = some v <|> pure [] // // Zero or more. return(Some(mayhap).Otherwise(EmptyEnumerable <T>())); }
/// <summary> /// void /// <para>a = TSource, () = Unit</para> /// <para>Create a new f () from an f a by replacing all of the values /// in the f a by ().</para> /// </summary> public static Mayhap <Unit> Skip <TSource>( this Mayhap <TSource> @this) { // void :: Functor f => f a -> f () // void x = () <$ x // // void value discards or ignores the result of evaluation, such as // the return value of an IO action. #if STRICT_HASKELL return(ReplaceWith(Abc.Unit.Default, @this)); #else return(@this.Select(_ => Abc.Unit.Default)); #endif }
/// <summary> /// (<**>) /// <para>A variant of (<*>) with the arguments reversed.</para> /// </summary> public static Mayhap <TResult> Apply <TSource, TResult>( this Mayhap <TSource> @this, Mayhap <Func <TSource, TResult> > applicative) { // (<**>) :: Applicative f => f a -> f (a -> b) -> f b // (<**>) = liftA2(\a f -> f a) // // A variant of '<*>' with the arguments reversed. #if STRICT_HASKELL return(Invoke(applicative, @this)); #else return(applicative.Bind(f => @this.Select(f))); #endif }
// Hummm, infinite recursive calls. Clean up // https://stackoverflow.com/questions/7671009/some-and-many-functions-from-the-alternative-type-class // https://www.reddit.com/r/haskell/comments/b71oje/i_dont_understand_how_some_and_many_from_the/ /// <summary>some</summary> public static Mayhap <IEnumerable <T> > Some <T>(Mayhap <T> mayhap) { // some :: f a -> f [a] // some v = some_v // where // many_v = some_v <|> pure [] // some_v = liftA2 (:) v many_v // // some v = (:) <$> v <*> many v // // One or more. Func <T, IEnumerable <T>, IEnumerable <T> > prepend = (x, y) => y.Prepend(x); return(Pure(prepend).Invoke(mayhap, Many(mayhap))); }
/// <summary> /// fmap /// <para>a = TSource, b = TResult</para> /// <para>Create a new f b from an f a using the results of calling a /// function on every value in the f a.</para> /// </summary> public static Mayhap <TResult> Map <TSource, TResult>( Func <TSource, TResult> mapper, Mayhap <TSource> mayhap) { // fmap :: (a -> b) -> f a -> f b // // Examples: // fmap (+1) (Just 1) == Just 2 // fmap (+1) Nothing == Nothing #if STRICT_HASKELL throw new NotImplementedException("Functor fmap"); #else return(mayhap.Select(mapper)); #endif }
/// <summary> /// (<|>) /// </summary> public static Mayhap <T> Otherwise <T>(this Mayhap <T> @this, Mayhap <T> other) { // (<|>) :: f a -> f a -> f a | infixl 3 | // // An associative binary operation. // // Examples: // Nothing <|> Nothing == Nothing // Just 1 <|> Nothing == Just 1 // Nothing <|> Just 1 == Just 1 // Just 1 <|> Just 2 == Just 1 #if STRICT_HASKELL throw new NotImplementedException("Alternative <|>"); #else return(@this.OrElse(other)); #endif }
/// <summary> /// ($>) /// <para>a = TSource, b = TResult</para> /// <para>Create a new f b, from an f a by replacing all of the values /// in the f a by a given value of type b.</para> /// </summary> public static Mayhap <TResult> ReplaceWith <TSource, TResult>( this Mayhap <TSource> @this, TResult value) where TResult : notnull { // ($>) :: Functor f => f a -> b -> f b | infixl 4 | // ($>) = flip (<$) // // Flipped version of <$. // // Examples: // Nothing $> "xxx" == Nothing // Just 1 $> "xxx" == Just "xxx" #if STRICT_HASKELL return(ReplaceWith(value, @this)); #else return(@this.Select(_ => value)); #endif }
/// <summary>(<&>)</summary> public static Mayhap <TResult> Map <TSource, TResult>( this Mayhap <TSource> @this, Func <TSource, TResult> mapper) { // [Functor] // (<&>) :: Functor f => f a -> (a -> b) -> f b | infixl 1 | // (<&>) = flip fmap // // Flipped version of <$>. // // Examples: // Just 1 <&> (+1) == Just 2 // Nothing <&> (+1) == Nothing #if STRICT_HASKELL return(Map(mapper, @this)); #else return(@this.Select(mapper)); #endif }
/// <summary> /// (*>) /// <para>Sequence actions, discarding the value of the first argument. /// </para> /// </summary> public static Mayhap <TResult> ContinueWith <TSource, TResult>( this Mayhap <TSource> @this, Mayhap <TResult> other) { // (*>) :: f a -> f b -> f b // a1 *> a2 = (id <$ a1) <*> a2 // // This is essentially the same as liftA2 (flip const), but if the // Functor instance has an optimized (<$), it may be better to use // that instead.Before liftA2 became a method, this definition // was strictly better, but now it depends on the functor.For a // functor supporting a sharing-enhancing (<$), this definition // may reduce allocation by preventing a1 from ever being fully // realized. In an implementation with a boring (<$) but an optimizing // liftA2, it would likely be better to define (*>) using liftA2. #if STRICT_HASKELL return(Lift(Thunks <TSource, TResult> .Const2).Invoke(@this, other)); #else return(other); #endif }
// Identity // pure id <*> v = v public static bool IdentityRule <T>(Mayhap <T> mayhap) { return(Pure(Thunks <T> .Ident).Invoke(mayhap) == mayhap); }