How to return an instance of a composite type based on a given field value?

Hi I’m going through the Julia manual and learning about abstract and composite types. I wanted to create a method that returns the instance of a composite type based on a given field value. I was wonder if there was a general way to do this. So for example suppose

 abstract type warrior end 
 struct elf <: warrior
     strength :: Int
     power :: String 
 end 
 struct ninja <: warrior
     strength:: Int
     Power:: String 
 end

Legolas = elf(10, "archery")
snakeeyes = ninja(12, "stealth")

battle(x::warriors, y::warrior) = max(x.strength,y.strength) 

This would return the max of the field x.strength. Is there a simple way to have a method return the instance based on a given field value as opposed to the value itself? i.e. in this case the battle method would return snakeeyes as opposed to 12? (please excuse the cheesy example, just trying to understand the purpose behind abstract types a little better.)

1 Like

Is this what you are looking for?

julia> battle(x::warrior, y::warrior) = x.strength > y.strength ? x : y
battle (generic function with 1 method)

julia> battle(Legolas, snakeeyes)
ninja(12, "stealth")
3 Likes

yes thanks so much for pointing this out! although is there a way for it to return the instance of the composite type, i.e. snakeeyes as opposed to just the composite type itself? I suppose I could add another field to the composite type including the name of the instance but I was wondering if there was a more direct way to go about it?

It already returns the instance, it’s just that you would need a field for the name if you want to see that.

1 Like

A way to realize by following the path you were attempting

julia> import Base.isless

julia> isless(x::warrior, y::warrior) = x.strength < y.strength
isless (generic function with 44 methods)

julia> battle(x::warrior, y::warrior) = max(x,y) 
battle (generic function with 1 method)

julia> battle(Legolas, snakeeyes)
ninja(12, "stealth")

julia> Legolas > snakeeyes
false

julia> Legolas < snakeeyes
true
2 Likes

Would it be possible to achieve this by adapting the show() function for the warrior type?

thanks, as mentioned I was wondering if there was a way to do it without having to add another field to the instance?

thanks! does this affect a global change to the <operator?

So you want to print the variable name, together with the value? For that you can use the @show macro:

julia> @show snakeeyes;
snakeeyes = ninja(12, "stealth")

This isn’t really a Julia question, though; you would have the same problem in any language. You are using a variable name to indicate the name of the individual warrior, which is a bit strange. If you want to associate a “person name” with a warrior, you should make a name field in the struct. Variable names aren’t really part of the data here, they are just labels that make it easy for you to write your code, but the @show macro gives you a sort of workaround for printing the variable name along with the value.

4 Likes

No, because the values of variables are not aware of the variables’ names.

You could do this with metaprogramming, such that

julia> @battle Legolas Snakeeyes
Snakeeyes wins 12 to 10!

but this is not the simplest way to do it. It’s likely to be difficult to read, difficult to write, difficult to debug and less performant than simply keeping track of names.

1 Like

A counterexample to show why you can’t expect variable names to follow the variables around:

julia> snakeeyes = Ninja(12);
julia> winner = snakeeyes

Should Julia print "winner" or "snakeeyes"?
How about

julia> function give_promotion!(employee::Warrior) 
employee.strength += 1
return employee
end;
julia> give_promotion(snakeeyes)

Should it print "snakeeyes" or "employee"?

2 Likes

“Variables” → “values” for clarity?

1 Like

Thanks so much guys this is very helpful. I was confused because I assumed the name was the instance of the composite type. However I still don’t quite understand how it is that I can enter the name into the method or function without issue, but only the value is returned. Is there somewhere that explains in what point in time they are separated within the method? Also I am still not sure how the @show workaround would work in this case. If I try something like. battle(x::warrior, y::warrior) = x.strength > y.strength ? @show(x) : @show(y) I get the following output.

y = ninja(12, "stealth")
ninja(12, "stealth")```

Inside functions, the variable gets a local name (employee in my second example above). In fact, my first example also shows a reassignment of variable names. Either way, the compiler happily replaces these names with numerical memory addresses (and intermediate names), because they only serve the purpose of communicating to the programmer.

@show works because macros (like @show) are given access to the entered expression. You can think of @show snakeeyes as a special version of a function _show( :snakeeyes ) which first uses the supplied Symbol as is, and then evaluates it to extract its value.

2 Likes