When should a function accept a symbol as an argument?

The fact that it is a literal is a red herring. This optimisation will also happen if you use a variable:

const my_real_symbol = :real
f_symbol(x) = f(my_real_symbol, x)

In fact, in trying to construct this counter-example, I found that it will also happen with the string, if we modify the code slightly, and instead check if the strings compare equal with the “triple equality” operator, ===:

f(s::String, x) = s === "real" ? x + 1 : x + 1im

In this case, we now get:

julia> Base.return_types(f_string, Tuple{Int64})[]
Int64

I am actually not entirely sure if there is a way to construct two strings that are equal, but don’t compare equal with ===, so this example might be too simple.

Something symbols can which strings can’t, though, is appear in type parameters. In practice, you sometimes really need to use this fact for expressibility in a type system. For instance:

struct MyType{name} end
is_it_a(::MyType{name}, s) where {name} = s == name

x = MyType{:value}()
is_it_a(x, :number)  # returns false
is_it_a(x, :value)  # returns true

Another example is using Val from Base. You sometimes have situations where the best way to structure your code is something like:

@inline f(s::Symbol, x) = f(Val(s), x)
f(::Val{:real}, x) = x + 1
f(::Val, x) = x + 1im

The reason for writing code like that can either be due to readability, flexibility, or just because the compiler needs that bit of extra help.

So when you encounter an interface where you are asked to give it a symbol and not a string, these kinds of considerations may have been the underlying reason for choosing such an API. In other cases, as has been pointed out by others in this thread, it may just be that people have become so comfortable with symbols in the community that they don’t see them as something that presents an extra level of mental overhead. But it is the case that there are things you can do with symbols that you can’t do with strings. So I do not think you are ever going to convince people that you should never use them as function input. And I think that once you accept that they might sometimes be used, it is better to use them early and often. That way, users will encounter them, wonder what they are, and hopefully learn it one way or the other. Otherwise, it might end up being something esoteric in the language that feels like magic, when it really isn’t.

5 Likes