How to generate methods based on different types of the parameter?

For example, I have the following code

using FilePaths

macro iostream_to_lines(methodname)
    return quote
        function $methodname(io::IOStream)
            $methodname(readlines(io))
        end
    end
end  # macro iostream_to_lines

macro path_to_iostream(methodname)
    return quote
        function $methodname(path::AbstractPath)
            isfile(path) && isreadable(path) || error("File $(path) not readable!")
            open(path, "r") do io
                $methodname(io)
            end
        end
    end
end  # macro path_to_iostream

function do_something(lines::Vector{String})
    println(first(lines))
end
@iostream_to_lines do_something
@path_to_iostream do_something

I want to generate methods for do_something when the input is an AbstractPath and when the input is an IOStream, finally it falls back to do_something(lines::Vector{String}).

However, this does not work. @iostream_to_lines do_something and @path_to_iostream do_something just generate 2 anonymous functions and they do not add methods to do_something.

I have considered using the @generated macro, but I have 2 difficulties:

  1. I have a lot of do_something-like functions, each of them differs from the others, so I have to write explicitly each do_something-like function for lines as input.
  2. The @generated macro requires the input of do_something to have the same name, e.g., x,
    julia> @generated function bar(x)
               if x <: Integer
                   return :(x ^ 2)
               else
                   return :(x)
               end
           end
    bar (generic function with 1 method)
    
    But I have different names for each method: lines::Vector{String}, io::IOStream and path::AbstractPath, they cannot be the same. What should I do?

I tried to write a minimal example. My question is equivalent to

macro mm()
    return quote
		global a(::Int) = 2
	end
end

julia> @mm()
a (generic function with 1 method)

julia> @mm()(2)
2

julia> a
a (generic function with 1 method)

julia> a(2)
2

This works. But it does not work for a multiline function a:

macro mm()
    return quote
		global function a(::Int)
			return 2 
		end
	end
end

julia> @mm()
#1#a (generic function with 1 method)

julia> a
ERROR: UndefVarError: a not defined

Why?

sorry but why are you generateing them…you can just… define them?

julia> function do_something(a::AbstractArray)
           return a[end]
       end
do_something (generic function with 1 method)

julia> function do_something(a::Vector{String})
           return a[1]
       end
do_something (generic function with 2 methods)

julia> do_something(["hi" "there"; "good" "day"])
"day"

julia> do_something(["hi", "good", "day"])
"hi"

Hi, I am using

function $methodname(path::AbstractPath)
    isfile(path) && isreadable(path) || error("File $(path) not readable!")
    open(path, "r") do io
        $methodname(io)
    end
end

and

function $methodname(io::IOStream)
    $methodname(readlines(io))
end

repeatedly on more functions like do_something(lines::Vector{String}), I want to reuse the 2 paradigms. The only differences between each of these functions are just the methodname.

You need to escape the input (see the manual on “macro hygiene”).

julia> macro iostream_to_lines(methodname)
           return quote
               function $methodname(io::IOStream)
                   $methodname(readlines(io))
               end
           end
       end  # macro iostream_to_lines
@iostream_to_lines (macro with 1 method)

julia> @macroexpand @iostream_to_lines do_something
quote
    #= REPL[14]:3 =#
    function #11#do_something(#12#io::Main.IOStream) # <------- mangled function name
        #= REPL[14]:4 =#
        #11#do_something((Main.readlines)(#12#io))
    end
end

julia> macro iostream_to_lines(methodname)
           return quote
               function $(esc(methodname))(io::IOStream)
                   $(esc(methodname))(readlines(io))
               end
           end
       end  # macro iostream_to_lines
@iostream_to_lines (macro with 1 method)

julia> @macroexpand @iostream_to_lines do_something
quote
    #= REPL[16]:3 =#
    function do_something(#13#io::Main.IOStream) # <----------- the name you want
        #= REPL[16]:4 =#
        do_something((Main.readlines)(#13#io))
    end
end
2 Likes