Val types vs singletons, is one easier for inference / compilation?

I’ve got some quantity which is inherently a singleton, and a function which returns it as part of a tuple. If I denote the singleton quantity by a type itself, I lose the type information (as expected):

struct Singleton end
foo() = (Singleton, 1)
@code_warntype foo() # Body::Tuple{DataType,Int64}

Somehow I have to hoist the information into the type system. I see two ways. Either denote the quantity by an instance of the type:

struct Singleton end
foo() = (Singleton(), 1)
@code_warntype foo() # Body::Tuple{Singleton,Int64}

or use a Val-type:

struct Singleton end
foo() = (Val(Singleton), 1)
@code_warntype foo() # Body::Tuple{Val{Singleton},Int64}

I’m wondering if in general one strategy or the other is easier on the compiler / on inference? From some benchmarks of my non-MWE version of this, I don’t really see any difference, but wondering if I might hit something as the code gets more complex (thinking along the lines of recursion limits, order-dependent inference issues, general slowness, etc…). Thanks for any insights.

They should be about the same.

1 Like

You are better off without the Val here – just additional syntax without benefit.
Val used to be used more often than it is nowadays. Julia’s internals improved substantively; some prior uses of Val are no longer impactful,

1 Like

Of course you could mimic nothing and missing and do

const singleton = Singleton()

if you want syntax like your first example.