Singleton Types

Often I want to use zero-sized structs for dispatch purposes, and it’s kind of annoying to have to make up a name for both the struct and the unique instance of that struct.

In Scala, they have object as a primitive, which creates a class and the unique instance of it in one go. The class is then anonymous, and printed out as Foo.type (or something like that, I can’t remember exactly what).

Would something like this be possible in Julia? It wouldn’t be that hard to do with a macro, using gensym for the name of the struct; but then would it be possible to overload printing out the name of the type to be something more informative than the gensym name?

If your singleton type is struct Foo end then the name of the unique instance of it is Foo(). Seems short enough, no?

4 Likes

Already is—because functions are singleton objects (although it’s a bit awkward for this):

julia> function foo end
foo (generic function with 0 methods)

julia> f(x) = x
       f(::typeof(foo)) = "Foo!"
f (generic function with 2 methods)

julia> f(foo)
"Foo!"

For with Julia, all things are possible:

julia> macro singleton(s::Symbol) sT=Symbol("#$s"); esc(quote
           struct $sT end
           Base.show(io::IO, ::$sT) = print(io, $("$s"))
           const $s = $sT()
       end) end
@singleton (macro with 1 method)

julia> @singleton bar
bar

julia> f(::typeof(bar)) = "Bar?"
f (generic function with 3 methods)

julia> f(bar)
"Bar?"

All that said, I’ve never found a reason not to just follow this:

julia> struct Baz end

julia> f(::Baz) = "Baz 😉"
f (generic function with 4 methods)

julia> f(Baz())
"Baz 😉"

This aligns more closely with how non-function singletons are already used throughout the language—meaning there’s less surprise, so unless there’s a good reason I’d stick with this.

1 Like

When I see something called with parentheses, I assume that it could be doing something, which scares me. But maybe I just have to get over that :stuck_out_tongue:.

When you see a capitalized name calling something with parentheses, you may assume it’s a constructor, which returns an object. If there are no input arguments, it’s quite likely a singleton (though it could, admittedly, be a non-singleton with default parameters values.)

I don’t believe it’s good practice to have capitalized functions that have observable side effects, if that’s what you are thinking.

But if you do need this, isn’t that very straightforward?

const mysingleton = MySingleton() 
3 Likes

I think it’s that lowercase name there that the objection is to.

It’s not an undue mental burden, is what I meant (and is what I thought was the problem). It is a little bit of typing, of course.