@match type?

I’m utterly confused why the following code doesn’t produce identical results (i.e. selecting on type with @match (Match.jl) or if…elseif…end). With @match (get_fmt1) regardless of the actual type, always an integer type is matched…? More insight much appreciated.

using Match


function get_fmt1(a)
  T = typeof(a)
  S = supertype(T)
  println("Ta, Sa = $T, $S")
  fmt(a) = @match S begin
    # Signed => "d"
    Integer || Signed || Unsigned || Bool || BigInt => "d"
    AbstractFloat                           => "f"
    AbstractString                          => "s"
    _,Any                                   => "s"
  end
  fmt(a)
end

function get_fmt2(a)
  T = typeof(a)
  S = supertype(T)
  # println("Ta, Sa = $T, $S")
  if S in [Integer Signed Unsigned Bool BigInt ] return "d"
  elseif S == AbstractFloat   return "f"
  elseif S == AbstractString  return "s"
  else return "o"
  end
end

trials = [ 1 1.0 "Foo" Inf ]

println("FMT 1")
for a in  trials
  Ta = typeof(a)
  Sa = supertype(Ta)
  println("a, Ta, Sa = $a, $Ta, $Sa")
  println("fmt a = ", get_fmt1(a))
end

println("FMT 2")
for a in  trials
  Ta = typeof(a)
  Sa = supertype(Ta)
  println("a, Ta, Sa = $a, $Ta, $Sa")
  println("fmt a = ", get_fmt2(a))
end

It seems that @match handles types not as values, and expressions like

julia>  macroexpand(:(@match S begin
                      AbstractFloat => "f"
                      Integer  => "d"
                      AbstractString => "s"
                      _ => "s"
        end))
quote  # REPL[53], line 2:
    "f"
end

always return the first match. You may want open an issue on Github for the library, to see if this is intended or a bug.

In the meantime, you can work around the problem with

function type_letter(S)
    @match S begin
        _::Type{Integer} || _::Type{Signed} || _::Type{Unsigned} ||
            _::Type{Bool} || _::Type{BigInt} => "d"
        _::Type{AbstractFloat} => "f"
        _::Type{AbstractString} => "s"
        _ => "s"
    end
end

which works as

julia>  [type_letter(supertype(typeof(x))) for x in [1 1.0 "Foo" Inf]]
1×4 Array{String,2}:
 "d"  "f"  "s"  "f"

Finally, while your actual use case may differ from the MWE, I am not sure that pattern matching is the best way to approach this.

@Tamas_Papp The “Julia proper way” would be to dispatch type_letter(S) on typeof(S) (…you notice I’m coming from the Python world…), the match behavior still seems problematic to me – I have posted an issue to the Match.jl github repos.

I think that type_letter dispatches on a type, see the example below it. Depending on how you plan to extend this, consider something like

type_letter{T <: Integer}(::Type{T}) = "d"
type_letter(::Type{AbstractFloat}) = "f"
type_letter(::Type{AbstractString}) = "s"
type_letter(_) = "s"            # fallback

get_fmt{T}(a::T) = type_letter(supertype(T))

Also, depending on your use case, consider

get_fmt{T <: AbstractFloat}(x::T) = "f"
get_fmt{T <: Integer}(x::T) = "d"
get_fmt{T <: AbstractString}(x::T) = "s"
get_fmt(x) = "s"                # fallback

get_fmt.([1 1.0 "Foo" Inf])

@Tamas_Papp The true julian/dispatch (very un-pythonic/test) way. Thanks.