foo(x::Real) = x
y = foo(3.4)
(y, typeof(y))
(3.4,Float64)
In this case, which is maybe the most common one, ::Real
signifies that this method definition will be applicable for all arguments that fulfill isa(x,Real)
. However, since the compiler will actually generate a definition for each type you use it with, if you call e.g. foo(3.4)
you will actually call something compiled as foo(x::Float64)
, which satisfies typeof(x)==Float64
. Because of this, the output will also be in this specific type.
bar(x) = x::Real
y = bar(3.4)
(y, typeof(y))
(3.4,Float64)
If you call bar(3.4)
with this definition, you will (just as above) get a version which compiles for typeof(x)==Float64
and returns x
. However, the compiler can also compile a version for bar("3.4")
, where typeof(x)==String
, which it couldn't do for foo
.
If you call the two versions above, you'll see different behavior for things that aren't some subtype of Real
:
foo("3.4")
no method foo(ASCIIString) while loading In[3], in expression starting on line 1
bar("3.4")
type: typeassert: expected Real, got ASCIIString while loading In[4], in expression starting on line 1 in bar at In[2]:1
In the first case, you get a when MethodError because the compiler can't even generate a method with the signature you asked for. In the second case you get a failed type assert instead, because the compiler can generate the method for you, but once you actually run it, the variable x
doesn't fulfil the assertion x::Real
.
function foobar(x)
y::FloatingPoint = x
y
end
z = foobar(3)
(z, typeof(z))
(3.0,Float64)
Note that foobar(3)
works even though we don't even have isa(3,FloatingPoint)
- there is no requirement on where in the type hierarchy the type of x
is.
Even though we have no requirement of where x
is in the type hierarchy, successful execution of y::FloatingPoint = x
is not without requirement - it works for this case because there is a conversion rule from typeof(3)
(i.e. Int64
) to FloatingPoint
(which will end up being Float64
on most architectures). Calling with an argument that does not have such a conversion still yields an error:
foobar("3.4")
no method convert(Type{FloatingPoint}, ASCIIString) while loading In[6], in expression starting on line 1 in foobar at In[5]:2
We could define such a conversion, though, and make it work.
import Base.convert # required to extend functions in Base with new methods
function convert(::Type{FloatingPoint}, s::ASCIIString)
if s=="3.4"
return 3.4
else
throw(ErrorException("I'm too lazy to make anything else work"))
end
end
convert (generic function with 592 methods)
We've now extended the function Base.convert
with another method, that tells Julia how to handle conversion from ASCIIString
to FloatingPoint
. (Of course, it only handles the very specific case of the string "3.4"
, but it's enough for our purposes at the moment.) We make another attempt:
foobar("3.4")
no method convert(Type{FloatingPoint}, ASCIIString) while loading In[8], in expression starting on line 1 in foobar at In[5]:2
Hm. Same error message... Let me copy-paste the code for the foobar
function to here, and try again:
function foobar(x)
y::FloatingPoint = x
y
end
foobar("3.4")
3.4
It works!
I might be wrong (and if I am, please let me know by filing an issue or submitting a pull-request to the GitHub repo where this notebook is hosted) but I think this is because Julia already compiled a version of foobar
for input arguments of type ASCIIString
, and even though we had defined a new method for convert
that should have changed the behavior of foobar
, there was no way for Julia to know that we needed to recompile it. When we executed the new method definition we replaced the old one with an identical copy that hadn't been compiled for ASCIIString
arguments yet, and so when we called foobar("3.4")
the last time we could take advantage of our new conversion method. So this behavior really has nothing to do with the ::
syntax, but just with the way I structured this post.
Note also that we didn't have to change the definition of foobar
at all - it still has exactly the same declaration of a variable y
with type FloatingPoint
, and assigning x
to it - but we taught Julia how to assign the value of a variable of type ASCIIString
to a variable of type FloatingPoint
by defining a conversion rule.