Indeed, that is why I stated it would have to have some resolved step.  :-)

Like I mentioned earlier:

> You could potentially sneak in some tricks though.  If you use something 
> like protocols (though something new, not protocol) to generate a call tree 
> in a single module, a single entrance function that matches based on both 
> of the types (properly ordered so the beam can efficiently match well) 
> would get it down to log(n).  Hmm, although if you have a *lot* of 
> potential matches (and I mean a **LOT**) then you could dispatch into a map 
> and that 'might' be faster (though honestly I would not bet on it).  This 
> would require a post-compile step like protocols do though (is there a 
> pattern to do those kind of things with elixir or are protocols just 
> 'special'?).  For normal macro's the best you could really get is to 
> manually specify everything in a single file in your base project, just 
> import the other modules that define matchers, and have a macro build up 
> the same call tree.
>

Thus unless an after-compile step is done a work-around for now is to be 
more explicit, perhaps something like this:
```elixir

# Let's define our own things from earlier:
defcompare Car.Compare do # Whatever namespace you want, let's just put each in 
their own .Compare
  defcompare (%Car{top_speed: top_speed0}, %Car{top_speed: top_speed1}), do: 
compare(top_speed0, top_speed1)
  defcompare (%Car{}, %Bike{}), do: :gt
  defcompare (%Car{}, %Truck{}), do: :ltend

defcompare Bike.Compare do
  defcompare (%Bike{gears: gears0}, %Bike{gears: gears1}), do: compare(gears0, 
gears1)
  defcompare (%Bike{}, %Car{}), do: :lt
  defcompare (%Bike{}, %Truck{}), do: :ltend
# Let's define trucks inline below, just because

defcompare Compare do # Let's make a top-level Compare (no one should do this 
unless in a final project, non-library  # Bring in other libraries if they have 
definitions, otherwise define them all inline
  defcompare Timex.Compare
  defcompare OtherStuff.Compare
  defcompare Car.Compare
  defcompare Bike.Compare
  # Truck inline, just for an example:
  defcompare (%Truck{wheels: wheels0}, %Truck{wheels: wheels1}), do: 
compare(wheels0, wheels1)
  defcompare (%Truck{}, %Bike{}), do: :gt
  defcompare (%Truck{}, %Car{}), do: :gtend

```

At which point you would have a `Car.Compare.compare/2`, 
`Bike.Compare.compare/2`, and a `Compare.compare/2` or whatever it should 
be named as.  Each `defcompare` at the module-level defines a public 
`compare/2` function in addition to something like a `__comparedefs__/0` 
method that returns, oh, say a useful AST that can be used in embedded it 
elsewhere.  Have the defcompare module-level macro parse over all its 
internal defcompare calls, if it is a module then import its ast and hold 
on to it, if it is like a function definition (without the name) then add 
those AST's to it as well, and finally merge those all together ordering by 
the first argument, then the second, and the when tests last for general 
efficiency (better ways of doing it but this will work best in the majority 
of cases).

It is unlike the prior example code in that it would move the final 
'Compare' thing to the user of which they should have their final version 
be named something uniform, like `Compare` that everything else could call, 
only the final parent application of a project should define a `Compare`, 
no library or so.  It is an ugly limitation but it is very efficient and 
that limitation is entirely removed if a post-process compile step like 
protocols is added, and someone could indeed write such a compiler as well, 
just have to add it to a main projects mix.exs compilers.


On Monday, August 29, 2016 at 5:32:40 PM UTC-6, Wiebe-Marten Wijnja wrote:
>
> Have you read the code of the implementation 
> <https://github.com/Qqwy/elixir_experimental_comparable/blob/master/lib/comparable.ex>
>  
> that was talked about earlier in this topic? It works in a way that is 
> similar to the solution you propose, with the difference that dispatching 
> does not work based on a pattern match, but based on the extracted struct 
> names (and for the built-in types, guard-clause matches).
>
> I do not think your solution (e.g. 'some macro' that outputs the code 
> snippet you outlined)  is able to work across projects, where Project A 
> defines a data type, Project B defines a data type, Project C defines a 
> frequent way of comparing A and B, and A, B and C are used by some person 
> in his personal project D, together with another custom data type D that 
> should be able to compare to both A and B as well.
> The only way for all of these data types to work together, is to make 
> something that can be extended after its original definition was made. It 
> is not possible to 'add more clauses to a function' after the module it is 
> contained in is closed and compiled. This is the fundamental problem with 
> and the fundamental difference in the solution you have proposed.
>
>
> On Tuesday, August 30, 2016 at 12:52:49 AM UTC+2, OvermindDL1 wrote:
>>
>> On Monday, August 29, 2016 at 3:14:04 PM UTC-6, José Valim wrote:
>>>
>>>
>>>> This could indeed be faster, however you'd have to get everything to 
>>>> agree on a single representation (a special tuple format perhaps? but then 
>>>> how would that rank with other tuples?).   
>>>>
>>>
>>> You don't have to agree on a format though. Imagine Decimal defines its 
>>> own format and Rational defines another, as long as at some point you 
>>> implement each other protocols, anywhere, it will work just fine. That's 
>>> the beauty of protocols after all! In the worst scenario, it will 
>>> degenerate to the case we are talking about right now: (n * (n - 1) / 2) 
>>> implementations?
>>>
>>
>> They certainly could do that yep, but the code to compare their formats 
>> still has to be somewhere, all my suggestion does is state where that 
>> somewhere should be (a prolog'y style relationship description, which 
>> erlang is awesome at and well optimized for), with only a single module 
>> (but potentially a lot of matches, which if organized well will be *very* 
>> efficient, log(N)).  :-)
>>
>>
>> On Monday, August 29, 2016 at 3:35:51 PM UTC-6, Wiebe-Marten Wijnja wrote:
>>>
>>> What we miss when doing a pre-compile step, is things like:
>>>
>>> {A} should be comparable with {B}, {B} should be comparable with {C}, 
>>> but {A} and {C} cannot be compared in a meaningful way.
>>> I cannot think of a practical example right now, but I think it is a 
>>> valid problem.
>>>
>>
>> My suggested method does support this (it would throw some error if {A} 
>> and {C} were compared).
>>
>>
>> On Monday, August 29, 2016 at 3:35:51 PM UTC-6, Wiebe-Marten Wijnja 
>> wrote: 
>>
>>> Also, when a third data type needs to be added to an existing 
>>> intermediate representation, how can this be done? There will be cases in 
>>> which this existing intermediate representation is no longer valid, but 
>>> you're unable to change the protocol implementations of those other types, 
>>> so you're stuck.
>>>
>>
>> Yeah just like with protocols, no real good way to update it without 
>> hot-code swapping. 
>>
>>
>> On Monday, August 29, 2016 at 3:35:51 PM UTC-6, Wiebe-Marten Wijnja wrote:
>>
>>> I also think that there are many cases in which it might be impossible 
>>> to come up with a proper intermediate format. We have both Enum.sort_by 
>>> (which uses a Schwarzian Transform, i.e. it first creates an intermediate 
>>> representation for all data elements and then sorts based on these) and the 
>>> normal Enum.sort. Although it usually is faster, there are cases where it 
>>> is impractical or impossible to use Enum.sort_by.
>>>
>>> An example: What about cases like Rock, Paper Scissors? Rock < Paper, 
>>> Paper < Scissors, Scissors < Rock? Although there is no *complete* order 
>>> for these data structures, we *do* want to be able to compare 
>>> individual pairs.
>>>
>>
>> Which my suggestion also supports.  Would be an interesting trouble for a 
>> fully typed system though...
>>
>>
>> On Monday, August 29, 2016 at 3:35:51 PM UTC-6, Wiebe-Marten Wijnja wrote:
>>
>>> Another nice edge case that would go completely wrong with an 
>>> intermediate-representation approach, would be NaN, which is unequal to 
>>> everything else, including itself.
>>>
>>
>> Which my suggestion could also support (perhaps by throwing an error, or 
>> return a :non_comparable atom or something, whatever was wanted).
>>
>>
>> On Monday, August 29, 2016 at 3:35:51 PM UTC-6, Wiebe-Marten Wijnja wrote:
>>
>>> So, I think it is paramount to create a multiprotocol or multimethod 
>>> <http://clojure.org/reference/multimethods>-like approach that works on 
>>> the *relationship or combination *of the two types being compared. On 
>>> an *edge* of the graph, rather than on a node like normal protocols do. 
>>> After all, a comparison is something that is about the relationship between 
>>> two things. You cannot compare a single thing.
>>>
>>
>> My suggestion follow a very prology way, which is precisely relationship 
>> descriptions (which erlang is awesome at).
>>
>>

-- 
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/75466b9a-5cb7-4098-a816-cfe0df2e0453%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to