Home > Software design >  Julia how to extend isless function
Julia how to extend isless function

Time:12-12

I am new to Julia and just learned that Julia can generate generic methods by changing the function signature.

function round_number(x::Float64)
  return round(x)
end

round_number(3.1415)
round_number(3)

function round_number(x::Int64)
  return x
end

round_number(3)

When I found the function test_interpolated cannot work with number and string, I tried to see if I can generate a new signature to extend the isless. Unfortunately, it did not work.

function test_interpolated(a, b)
  if a < b
      "$a is less than $b"
  elseif a > b
      "$a is greater than $b"
  else
      "$a is equal to $b"
  end
end

test_interpolated(3.14, 3.14)
test_interpolated(3.14, "3.14")

function isless(x::AbstractFloat, y::AbstractString)
  return x < float64(y)
end

test_interpolated(3.14, "3.14")

Did I misunderstand the concept?

CodePudding user response:

First note that there is no float64 function in Julia. To parse a string as a number you need to use the parse function:

parse(Float64, "3.14")

The second thing in your code is that:

function isless(x::AbstractFloat, y::AbstractString)
  return x < float64(y)
end

creates a new function named isless in your current module (most likely Main if you are working in e.g. REPL).

To add a method to the isless function defined in Base module you would have to write:

function Base.isless(x::AbstractFloat, y::AbstractString)
  return x < float64(y)
end

(note the Base. prefix)

Although Julia allows for it you should never do it in your code. This style is called type piracy, and is explained here.

In short you are adding a method to a function from Base module and both AbstractFloat and AbstractString are also defined in Base module. The problem is that adding such a method might break other code that assumes in this case that isless when passed AbstractFloat and AbstractString throws an error.

The way you should define your function is e.g. as follows:

toreal(x::Real) = x

toreal(x::AbstractString) = parse(Float64, x)

function test_interpolated(a::Real, b::Real)
  if a < b
      "$a is less than $b"
  elseif a > b
      "$a is greater than $b"
  else
      "$a is equal to $b"
  end
end

test_interpolated(a, b) = test_interpolated(toreal(a), toreal(b))

In this way you ensure that you have one specific method with narrow signature implementing the logic for real numbers and other method for test_interpolated that has a wide signature that performs conversion to appropriate types.

Note that I use Real not AbstractFloat as comparison with < is guaranteed to be defined across two Real values, and you most likely want your function to e.g. accept integers.

The approach I show is explained here.

  • Related