String constructor vs string function

Context:

I have two enums as follows:

@enum HouseSize SMALL_HOUSE MEDIUM_HOUSE LARGE_HOUSE
@enum HouseEfficiency LOW_EFFICIENCY MEDIUM_EFFICIENCY HIGH_EFFICIENCY

If I just had the HouseSize enum, then I could simplify the levels to SMALL, MEDIUM, and LARGE, but since I also have HouseEfficiency, I need to disambiguate the MEDIUM levels. Anyhow, for use in plot titles I’ve implemented a String constructor as follows:

function String(hs::HouseSize)
    if hs == SMALL_HOUSE
        "small"
    elseif hs == MEDIUM_HOUSE
        "medium"
    else
        "large"
    end
end

Question:

I’m also aware of the string function, which turns arbitrary objects into strings by using their print methods. Should I be overloading print instead of String?

Best practice is probably to overload show instead. I’m not entirely sure how you’re using this in plot titles, but in general if you’re just interested in displaying something rather than getting a String to do further processing on, it’s probably better to use the show/display machinery.

As an additional note, any reason you can’t substitute a single enum for your two as shown below? Seems cleaner to me since the enums are representing relatively similar things. I guess if you’re really attached to displaying “small” for sizes and “low” for efficiency, that wouldn’t work.

@enum Levels SMALL MEDIUM LARGE
const LOW = SMALL

I’m using something like the following for plot titles (yes, I know I could be using Plot Recipes):

function plot(::MyType; kwargs...)
    plot(
        ...,
        title = "blah blah $(String(size)) size house, $(String(efficiency)) efficiency"
    )
end

So, I don’t think this is a use-case for show. I should probably overload print and do

title = string("blah blah ", size, " size house, ", efficiency, " efficiency")

I have various methods that dispatch on ::HouseSize and ::HouseEfficiency, so I need both.

Actually, this is a perfect use-case for defining a show method, and you almost never want to overload print (the standard definition of print is basically print(io::IO, x) = show(io, x) with error handling).

If you define Base.show(io::IO, hs::HouseSize), you can directly do plot(..., title="blah blah $size").

EDIT: As for when you might want to define print rather than show, the docstring of print says:

print falls back to calling show, so most types should just define
show. Define print if your type has a separate “plain” representation.
For example, show displays strings with quotes, and print displays strings
without quotes.

2 Likes

Oh yeah, good point about the interpolation! I had a brain fart there. Though that interpolation syntax also works properly if you overload print instead of show.

To add to your quote from the docstring:

The representation used by print includes minimal formatting and tries to avoid Julia-specific details.

So it all comes down to whether I think the print/show method that I’m defining is a separate “plain” representation that avoids Julia-specific details. That’s a bit of a grey area, but in this case it seems to me that I’m defining the “plain” representation of my enum levels.

Also, of course, if I overload show it will affect printing at the REPL. In this case, I would prefer REPL printing to fall back to the show method for Enum. That won’t happen if I overload show:

julia> @enum HouseSize SMALL_HOUSE

julia> SMALL_HOUSE
SMALL_HOUSE::HouseSize = 0

julia> Base.show(io::IO, ::HouseSize) = print(io, "small")

julia> SMALL_HOUSE
small

It’s interesting to see what happens to REPL printing when I overload print instead of show:

julia> @enum HouseSize SMALL_HOUSE

julia> SMALL_HOUSE
SMALL_HOUSE::HouseSize = 0

julia> Base.print(io::IO, ::HouseSize) = print(io, "small")

julia> SMALL_HOUSE
small::HouseSize = 0

Now the prefix in the REPL output went from SMALL_HOUSE to small! Apparently the Enum show method calls print for that portion of the output.