Just playing around with a possible typeclass-style usage:
```elixir
defclass Kernel.Eq do
where do
@operator == # Assuming there is a way to define an operator or so to call
the next function, otherwise ignore lines like @operator
eq?(any, any) :: boolean()
end
defproperty equals_self(e) do
true == eq?(e, e)
endend
defclass Kernel.Ord do
extend Kernel.Eq
where do
compare(any, any) :: boolean()
end
defproperty compare_test(l, r) do
compare(l, r) == case compare(r, l) do
:< -> :>
:> -> :<
:= -> :=
end
end
@operator ==
@spec eq?(any, any) :: boolean
def eq?(l, r), do: compare(l, r) == := # Implement Kernel.Eq's call
@operator <
@spec lt?(any, any) :: boolean
def lt?(l, r), do: compare(l, r) == :<
@operator <=
@spec le?(any, any) :: boolean
def le?(l, r), do: compare(l, r) in [:<, :=]
@operator >
@spec gt?(any, any) :: boolean
def gt?(l, r), do: compare(l, r) == :>
@operator >=
@spec ge?(any, any) :: boolean
def ge?(l, r), do: compare(l, r) in [:>, :=]
# Etc... whatever other things you want to implement on thisend
# Some implementation! I changed the syntax here to be more generically useful
definstance Kernel.Ord(l, r) when is_int(l) and is_int(r) do
def compare(l, r) do
cond do
l<r -> :<
l>r -> :>
l===r -> :=
endend
definstance Kernel.Ord(l, r) when is_float(l) and is_float(r) do
def compare(l, r) do
cond do
l<r -> :<
l>r -> :>
l===r -> :=
endend
definstance Kernel.Ord(l, r) when is_int(l) and is_float(r) do
def compare(l, r) do
cond do
l<r -> :<
l>r -> :>
l==r -> :=
endend
definstance Kernel.Ord(l, r) when is_float(l) and is_int(r) do
defdelegate compare(l, r), to: Kernel.Ord(r, l)end# Thought honestly for
basic types you'd probably just have a single Kernel.Ord instance# that tests
for any basic type and compares appropriately, like via erlang term ordering#
for everything except `is_struct`'s. Maybe like this:
definstance Kernel.Ord(l, r) when not is_struct(l) and not is_struct(r) do
def compare(l, r) do # Default to erlang term ordering for all non-struct
types
cond do
l<r -> :<
l>r -> :>
l==r -> := # What to do about float/int comparisons by default, leave it
loose like `==`, or tight like `===`?
endend
# A custom typedefmodule MyDate do
defstruct year: 0, month: 0, day: 0end# Lets support comparing our date with
any map/struct that has year/month/day keys!
definstance Kernel.Ord(%MyDate{}, %{year: _, month: _, day: _}) when do
def compare(d0, d1) do # delegate to the list compare because I'm lazy
Kernel.Ord.compare([d0.year, d0.month, d0.day], [d1.year, d1.month, d1.day])
endend# And let's support both orders
definstance Kernel.Ord(%{year: _, month: _, day: _}, %MyDate{}) when do
def compare(d0, d1), do: Kernel.Ord.compare(d1, d0)end
```
Or something like that...
On Tuesday, November 22, 2016 at 9:38:23 AM UTC-7, OvermindDL1 wrote:
>
> A few notes. :-)
> At:
> https://github.com/Qqwy/elixir_experimental_comparable/blob/master/lib/comparable.ex#L27-L37
> Shouldn't this:
> ```elixir
>
> defcomparison(Integer, RomanNumeral) do
> def compare(int, %RomanNumeral{num: num}) when num < int, do: :<
> def compare(int, %RomanNumeral{num: num}) when num > int, do: :>
> def compare(int, %RomanNumeral{}) , do: :=
> end
> ```
> Actually be something like:
> ```elixir
> defcomparison(Integer, RomanNumeral) do
> def compare(int, %RomanNumeral{num: num}), do: Comparable.compare(int, num)
> end
> ```
> Even for primitive types it can be a bad idea not to delegate the compare
> function (the compiler can optimize it or so, especially with macros).
> What if someone stuff something that is not an integer in the RomanNumeral
> somehow, they would then report as equal even when not. That brings up the
> thing, maybe it should be defined as:
> ```elixir
> defcomparison(Integer, %RomanNumeral{num: Integer}) do
> def compare(int, %RomanNumeral{num: num}), do: Comparable.compare(int, num)
> end
> ```
> The defcomparison line itself changed to be explicit as to what is
> accepted (which can be performed via a match/when internally). Instead of
> having automatic naming for things like Integer, you could also do this:
> ```elixir
> defcomparison(int, %RomanNumeral{num: num}) when is_int(int) and
> is_int(num) do
> def compare(int, %RomanNumeral{num: num}), do: Comparable.compare(int, num)
> end
> ```
> And considering that, maybe instead just entirely shrink it down to:
> ```elixir
> defcomparison(int, %RomanNumeral{num: num}) when is_int(int) and
> is_int(num) do
> Comparable.compare(int, num)
> end
> ```
> I.E., make the `defcomparison` call itself define the function, of which
> you could put multiple:
> ```elixir
> defcomparison(int, %RomanNumeral{num: num}) when is_int(int) and
> is_int(num) do
> Comparable.compare(int, num)
> end defcomparison(float, %RomanNumeral{num: num}) when is_float(float) and
> is_int(num) do Comparable.compare(float, num) end
> ```
> Although in this simple case you could simplify it down to just:
> ```elixir
> defcomparison(v, %RomanNumeral{num: num}) when is_int(num) do
> Comparable.compare(v, num)
> end
> ```
> This states that %RomanNumerals must always hold an integer, but you can
> compare anything to the integer that you could normally compare to an
> integer (whether another integer, float, some custom type, etc...).
>
> But the above latest syntax simplifies it, you could put multiple
> defcomparisons in a row like normal function heads (the 'use Comparable'
> declaration in the module can do the combining and optimizing work), you
> use normal Elixir syntax to deconstruct the types and put on when
> conditions, *and* you could potentially restrict the syntax to not allow
> costly calls so you could use compares in `when`'s, perhaps something as
> simple as like:
> ```elixir
> <table class="highlight tab-size js-file-line-container" data-tab-size="8"
> style="border-collapse: collapse; box-sizing: border-box; tab-size: 8;
> font-family:
>
--
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/61396ba4-b2df-4686-b755-c4505a28db54%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.