Just a quick remark (for anything more this requires a deeper analysis):

Why would erlang term ordering be important for the return value of compare/2 
function? The function you pass to Enum.sort should return true or false, so 
you can't use it there directly anyway.

Michał.

On 9 Jun 2017, 13:21 +0200, Wiebe-Marten Wijnja <[email protected]>, 
wrote:
> I would like to revive this topic, because I am still looking for a good way 
> to perform comparisons on custom structures.
>
> Right now there is no standardized way to do this, resulting in widely 
> varying APIs❧, many of which are incompatible with e.g. `Enum.sort` because 
> of the chosen output format, which would be one of the most obvious and 
> frequent use-cases of comparison operations.
>
> So, I would like to propose the following:
>
>
> • Add a `Comparable` protocol (Or behaviour?), containing 
> `Comparable.compare(MyStruct.t, MyStruct.t)`.
> • Choose a sensible output format for this compare function. This is 
> something we should discuss:
> • >
>     • In existing parts of Elixir Core (the (Date)(Time) modules), `:lt`, 
> `:eq`, `:gt` are used. But these do not themselves follow the Erlang Term 
> Ordering, and therefore cannot be used directly for things 
> like`Enum.sort(enumerable, &Kernel.compare/2)`.
>     • Many other languages, and multiple existing libraries return an integer 
> `-1, 0, 1`. This does follow the Erlang Term Ordering. Main disadvantage: 
> They are not very descriptive, someone might think that any integer could be 
> returned.
>     • `:<`, `:=`, `:>` are another possibility: Because of the ASCII 
> ordering, these follow the Erlang Term Ordering, and are arguably more 
> descriptive than the integers. Main disadvantage: They are not widely used 
> yet.
>     • What to do on error? Raise? Or return `nil`? `{:error, some_reason}` is 
> another possibility (Although we do not use an `{:ok, success}` here, so 
> maybe this is confusing?).
>
> • Add `Kernel.compare/2`, which is overridden to use simple comparison for 
> built-in datatypes, and dispatches to the protocol for structs (raising when 
> the structs are not of the same type).
>
> Aside from this, (this can be accepted/rejected independently!) I see value 
> in a `Comparable.coerce(builtin)`, which can be optionally implemented to 
> allow a builtin datatype to be converted to the struct of the other, before 
> comparing.
>
>
>
> ❧ About widely-varying APIs:
>
> - Time.compare, Date.compare, DateTime.compare return `:lt`, `:eq` or `:gt`
> - Timex.compare returns integer (-1, 0, 1) or {:error, reason} and has an 
> optional granularity.
> - Decimal.compare returns #Decimal<-1>, #Decimal<0>, #Decimal<1> or 
> #Decimal<NaN>.
> - Decimal.cmp returns `:lt`, :eq` or `:gt` and raises on NaN.
> - Ratio.compare returns integer (-1, 0, 1) or raises Ratio.ComparisonError.
>
>
>
>
> On Wednesday, December 14, 2016 at 12:49:26 PM UTC+1, Wiebe-Marten Wijnja 
> wrote:
> > I have been thinking longer about this.
> >
> > I think that the across-type implementation is overkill; I have a lot of 
> > trouble to come up with cases where this would be useful: Cases that I can 
> > think of can readily be solved by wrapping the inner structure in a 
> > containing struct, which is obviously more clear/explicit than providing a 
> > two-typed frankenprotocol.
> >
> >
> > I have been working on Numbers, which is basically dispatches arithmetic 
> > operations to any structs that implements its standardized Numeric 
> > behaviour. In this case, this means that functions/modules/structs can be 
> > written that wrap any kind of thing that implements the behaviour, which 
> > means that e.g. my Tensor library allows addition/multiplication to 
> > performed regardless of if the contents of the vectors/matrices/tensors are 
> > `Integer`s, `Float`s, `Decimal`s, `Ratio`nals or even `ComplexNum`bers.
> >
> > I think that such a standardized way constructing something still is very 
> > important in the core language, because a standardized API means that 
> > modules consuming the API can use any modules(/data types) that are 
> > exposing the API.
> >
> > I now envision the following, much simpler and less 'new language 
> > feature'-heavy than my original proposal:
> >
> > ---------------------
> >
> > There is a normal protocol called Comparable, exposing two functions that 
> > can be overridden:
> >
> > • compare(a, b) compares the two structs `a` and `b` of the same type. This 
> > function should return `:lt`, `:gt`, or `:eq` (keeping with the 
> > specification that DateTime.compare and Time.compare already follow). If 
> > there is no sensible way to compare the two types, a 
> > Comparable.CannotCompareError should be raised, with a describing error 
> > message. The Any implementation always raises this error.
> > • coerce(some_builtin_value).  Can optionally(!) be implemented to allow 
> > certain standard data types (e.g. numbers, or strings) to be automatically 
> > converted to the type of the thing we want to compare it with, to allow 
> > shorter notation for things like `compare(Decimal.new(2), 3)`. The Any 
> > implementation always raises Comparable.CannotCompareError.
> >
> > There is a new function in the Kernel function Kernel.compare(a, b) has the 
> > following variants:
> >
> > # struct <=> struct
> > Kernel.compare(a = %someStruct{}, b = %someStruct{}), do: 
> > Comparable.compare(a, b)
> > # struct <=> differentStruct
> > Kernel.compare(a = %someStruct{}, b = %someDifferentStruct{}), do: raise 
> > Comparable.CannotCompareError, message: "Cannot compare #{inspect(a)} with 
> > #{inspect(b)}."
> > # struct <=> builtin
> > Kernel.compare(a = %someStruct{}, b), do: Comparable.compare(a, 
> > Comparable.coerce(b))
> > # builtin <=> struct
> > Kernel.compare(a, b = %someStruct{}), do: 
> > Comparable.compare(Comparable.coerce(a), b)
> > # builtin <=> builtin, use Erlang's built-in term ordering.
> > Kernel.compare(a, b) when a == b, do: :eq
> > Kernel.compare(a, b) when a < b, do: :lt
> > Kernel.compare(a, b), do: :gt
> >
> >
> >
> >
> >
> >
> >
> >
> --
> You received this message because you are subscribed to the Google Groups 
> "elixir-lang-core" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/elixir-lang-core/c55bd3c0-a2a9-4955-a62f-9184a587a029%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/6bc6ef28-ea74-45c4-99c4-d7ff4c0154e8%40Spark.
For more options, visit https://groups.google.com/d/optout.

Reply via email to