Symmetry in arguments and default behavior

question

#1

Hello, suppose I have the following types

abstract type Food end

struct Milk <: Food end
struct Berries <: Food end
struct Fish <: Food end 

Now, I want to introduce a function mix with 1) a default behavior for unknown subtypes of Food and with 2) symmetry in the arguments.

For the 1) I could write this:

mix(A::Food, B::Food) = "don't know how to mix"

for the 2) I can go with:

mix(A:.Food, B::Food) = mix(B, A)

But I can’t use both at the same time. When follow up with this code:

mix(A::Milk, B::Berries) = "milk shake"

I can run

mix(Milk(), Berries())
mix(Berries(), Milk())

but not

mix(Fish(), Milk())

which ends up in the following:

ERROR: LoadError: StackOverflowError:
Stacktrace:
 [1] mix(::Milk, ::Fish) at /media/DATA/PhD/code/ising/ising_julia_code/sandbox.jl:155 (repeats 80000 times)
while loading /media/DATA/PhD/code/ising/ising_julia_code/sandbox.jl, in expression starting on line 243

Is it possible to do it in a clever way without implementing

mix(A::Food, B::Something)

for each Something <: Food type separately?

Thanks


#2

I first thought was to use:

mix(A::Food, B::Food) = objectid(typeof(A)) ≤ objectid(typeof(B)) ? "don't know how to mix" : mix(B,A)

However, this fails as you would get a problem when objectid(Berries) < objectid(Milk) as then the arguments would not be reversed as expected.


#3

Are you on julia 0.7? Because I don’t have this one?

However, the hash version works really nice.


#4

Yes objectid is on Julia 0.7. I have withdrawn the post as I have to work a bit on it as it is not fully correct yet. I will post an update.


#5

Ok, thanks for the quick reply anyway.


#6

So this seems to be a possible way to do it:

function mix(A::Food, B::Food)
    if which(mix, (typeof(B), typeof(A))) === which(mix, (Food, Food))
        if which(mix, (typeof(A), typeof(B))) === which(mix, (Food, Food))
            "don't know how to mix"
        else
            mix(A, B)
        end
    else
        mix(B, A)
    end
end

The inner if is to safeguard against the following call:

invoke(mix, Tuple{Food, Food}, Milk(), Berries())

#7

Could you please say, why using hashs or objectid was a bad way?


#8

Reverted the answer above with information why it is wrong.