louthy / language-ext

C# functional language extensions - a base class library for functional programming
MIT License
6.41k stars 415 forks source link

Best / easiest way to get Min/Max via Ord<A> #354

Closed StefanBertels closed 6 years ago

StefanBertels commented 6 years ago

Question: Could/should interface Ord contain method

[Pure]
A Min(A x, A y) => Compare(x, y) > 0 ? y : x

[Pure]
A Max(A x, A y) => Compare(x, y) < 0 ? y : x

Seems like this direct implementation in interface will be possible in future C# release. Currently interfaces have to be implemented in each implementing type.

This could be used as predicate for Reduce (and Fold) to get Min function on a sequence:

List("bla", "blub").Reduce(OrdStringOrdinalIgnoreCase.Inst.Min)

Or is there already something similar in LanguageExt?

(I thought about LINQ variant like list.OrderBy(...).First() but then I would need some OrderBy variant taking an Ord.)

My goals are: easy readable code (clear intent) and make use of ORD.

TysonMN commented 6 years ago

Can you link to the Ord interface?

StefanBertels commented 6 years ago

https://github.com/louthy/language-ext/blob/master/LanguageExt.Core/TypeClasses/Ord/Ord.cs

StefanBertels commented 6 years ago

If someone is interested: read about upcoming C# feature here: https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md

louthy commented 6 years ago

Hi Stefan, if you take a look at the Ord.Prelude.cs. You'll see various functions for working with Ord. It doesn't need to be in the interface itself as the behaviour is common to all types, and would add the burden of typing in the same method for every Ord class-instance. When default interface methods come along then there's definitely an argument for adding more to the interface of the type-classes, but right now I don't think it's wise.

We could add min and max relatively easily to the Prelude though:

public static A min<OrdA, A>(A x, A y) where OrdA : struct, Ord<A> =>
    compare<OrdA, A>(x, y) < 0
        ? x
        : y;

public static A max<OrdA, A>(A x, A y) where OrdA : struct, Ord<A> =>
    compare<OrdA, A>(x, y) > 0
        ? x
        : y;

public static A min<OrdA, A>(A x, A y, A z, params A[] tail) where OrdA : struct, Ord<A> =>
    fold<MArray<A>, A[], A, A>(tail, min<OrdA, A>(x, min<OrdA, A>(y, z)), min<OrdA, A>);

public static A max<OrdA, A>(A x, A y, A z, params A[] tail) where OrdA : struct, Ord<A> =>
    fold<MArray<A>, A[], A, A>(tail, max<OrdA, A>(x, max<OrdA, A>(y, z)), max<OrdA, A>);

In fact I've just done it (it's on v2.2.5-beta)

StefanBertels commented 6 years ago

Thanks. I agree. Just one point: I suggest returning first element on equality always. (stable order)