I am having trouble executing the code snippet below that was contributed on an old thread.
My objective is to define a struct that I can use to validate arguments inside function definitions. Any argument that is not the correct type should throw an error. The key point is that arguments are validated using types, not in the body of the function definition.
I realise I could validate the arguments in a separate “validation function”, which would be called in the body of each function definition. In my case this may provide a really neat, concise and readable solution as I have many functions with similar argument types.
value :: Int
function OddInt(x :: Int)
if isodd(x)
return new(x)
else
throw(DomainError("$x is not odd"))
end
end
end
function thisoddball(n :: OddInt)
return n
end
thisoddball(3)
The last call to thisodball(3) returns a method type error:
Error: Methoderror: No method matching this oddball(::Int64)
As I understand it …thisoddball is testing to see that the argument n is of type OddInt. The structure OddInt however is returning a type Int64, and so the it produces a Method Error.
Calling OddInt(3) or OddInt(2) work just as expected.
Calling a thisoddball(3) produces the method error described.
I should add that my situation has nothing to do with Odd numbers. This is just a self contained example that explains the issue as simply as possible.
Sorry the posted code snippet missed first line. I have reposted in full below.
struct OddInt
value :: Int
function OddInt(x :: Int)
if isodd(x)
return new(x)
else
throw(DomainError("$x is not odd"))
end
end
end
function thisoddball(n :: OddInt)
return n
end
thisoddball(3)
Thanks, passing an OddInt object does indeed work.
To help me understand … I was thinking that I could call “thisoddball(3)”. Not have to use “thisoddball(OddInt(3))”.
If I have the correct terminology, I thought that:
OddInt was an inner constructor for the structure OddInt??
(n :: OddInt) was a type assertion over the argument n?? In my head, that would call the OddInt constructor, and in the process it would execute any validation logic contained in that constructor (is n odd?). The “new” part of the constructor would then create the validated anonymous object??
Instead it feels like I am using a function (OddInt) as a parameter to another function “thisoddball”?
In a simpler scenario; for a function defined as mydouble(x::Int) = x * 2
I would make the call ‘mydouble(2)’, not `mydouble(Int(2))'.
Dispatch does not work that way. If you define a method foo(x::Bar), that means it will only accept inputs of type Bar (or subtypes of Bar if it is an abstract type), it will not attempt to convert input to Bar.
When you define
thisoddball(n :: OddInt) = n
and you call thisoddball(3), the compiler gets the type of the input, which is Int64, and checks if there is a method thisoddball(::Int). There is not such method, so you get an error.
What you can do instead if this:
thisoddball(n::OddInt) = n
thisoddball(n::Int) = thisoddball(OddInt(n))
As for this:
2 is already of type Int so mydouble(2) should work, while mydouble(2.0) or mydouble(0x2) should not.
Thank you both (DNF & mzaffalon) for thoughtful, concise responses. Both answers help me to better understand topics I have grappled with. Hopefully it may also help others.
DNF: (and similar answer from mzafflon) - where you propose what I could do instead… I think of this as multiple dispatch. In my head, you are defining two methods for thisoddball. One method with an OddInt Object argument type, the second with an Int argument type.
Other parts of your answers also resonated with me. (Not how dispatch works, doesn’t attempt to convert input, part of the definition of thisoddball…)