5
$\begingroup$

A Unit Type is one inhabited by exactly one value, and, due to the lack of possible discrimination ("no information able to be held"), is reducible to the 0-tuple.
Meanwhile a Singleton is one inhabited by exactly one value, and of which Set Theory says is a 1-tuple.

What are the differences between those two concepts, if any, and if there is none what is this difference in cardinality?
Do both coexist in the same nomenclatures?
And, given a (x: a) -> (y: b) -> (x | y) function type (read "takes an a called x and a b called y, and returns either x or y") where a and b are different, what is the correct and/or accepted terminology to refer to x and y?

$\endgroup$
3
  • $\begingroup$ Your link to "singleton" is to the mathematical meaning of that term (i.e. a set with one element), not to the meaning in computer programming. A "singleton" as in the OOP singleton pattern is not a one-tuple, it's a type with one value. $\endgroup$
    – kaya3
    Commented Jun 15, 2023 at 19:19
  • $\begingroup$ @kaya3-supportthestrike If you're referring to the Singleton Pattern as seen in OO, I would assume it is a particular implementation of the Set Theory concept. Am I wrong? $\endgroup$
    – Longinus
    Commented Jun 15, 2023 at 19:23
  • 2
    $\begingroup$ In set theory if you say something like "let S be a singleton set", this means S is a set of size 1, not that there is only one value that S can have. Singleton in everyday language means "something there is only one of", or "something that is alone; not part of a collection of multiple things", so the Singleton Pattern is more in line with the former meaning and the mathematical use of the word is more in line with the latter. The only connection is that for a unit type, the set of all possible values of that type is a singleton set; not that singleton sets are values of that type. $\endgroup$
    – kaya3
    Commented Jun 15, 2023 at 19:29

4 Answers 4

9
$\begingroup$

A unit type is one that explicitly has no information content: there is a single value with no further meaning, and it only indicates completion.

Singleton types retain (some) information content. For example, a number of languages permit using string or numeric literals as types that can hold exactly that value, and no other; these types can be meaningfully disjoined, so 1 | 2 may be the type that is either the number one or two; this will be a subtype of the integer type, and the value can be extracted to that type. The values of singleton types can be inspected and used as meaningful values in the customary ways of the language, while the value of the unit type is merely present.

Singleton types may also be a realisation of what would be an algebraic data type terminal in another language, as a special kind of constant with static checking. In some object-oriented languages, singleton types can be created from an object reference. Scala is one such language:

object A { def greeting = "Hello" }
var a : A.type = A
var x : Any = a
x match {
    case n : A.type => 1
    case _ => 2
}

Scala has the literal types from above too, representing singleton types of those literal values.

The relation to a mathematical singleton set is in the conception of types as sets of all their values; this is not universally the model of types people use, but is a very common one. A singleton type has one value and equates to the set of that value alone, but there can be many of these singleton types in the program. In some systems, every distinct value has a corresponding singleton type whether it is used or not.


The Singleton pattern as in the GoF is a way of enabling a single-valued value with chosen methods on it by inhibiting the creation of other instances of a class. These are sometimes sentinel values or used just for first-class values encapsulating shared state and behaviour, often not of the singleton type but a supertype. This is to some extent overloaded terminology; in a true singleton type, there is only one value, so constructing another one just doesn’t arise, and the pattern is an artefact of the classical structure of those languages.

$\endgroup$
5
$\begingroup$

What are the differences between those two concepts, if any, and if there is none what is this difference in cardinality?

One can consider a Unit type/set a one example of a Singleton type/set. In contrast to other singletons, Unit type (by definition/construction) has extremely boring value — a unit value that has no structure. In that sense it's isomorphic to an empty set or a 0-tuple.

In set theory other singleton values can have structure, in particular they might be sets themselves, and not even necessarily singleton sets. In type theory values of singleton types might have structure too, and thus can be inspected at run-time.

Do both coexist in the same nomenclatures?

There are too many different (albeit etymologically related) notions of a "singleton". Apart from already mentioned singleton sets and singleton pattern from OOP, there are singleton types from Haskell...

And, given a (x: a) -> (y: b) -> (x | y) function type (read "takes an a called x and a b called y, and returns either x or y") where a and b are different, what is the correct and/or accepted terminology to refer to x and y?

This is an instance of a Dependent Function (aka Π-type), and x and y are simply the (names of) parameters of a said function.

$\endgroup$
2
$\begingroup$

Type-theoretically, a 1-tuple is just isomorphic to whatever is contained in that tuple. For example tuple<int> is isomorphic to int: I can map every int to a unique tuple<int> and vice versa.

A singleton type is simply a type T where, every time the program is run, we only instantiate a single (traditionally mutable) value. It can have any shape that is needed for the program. I don't know of any language that encodes "singleton-ness" as an explicit static property, instead relying on things like private constructors and not exposing any methods that allow code outside of the implementation to indirectly create new instances. That doesn't mean T only has one element, it can have many elements, so it is not a singleton set. Just like the type containing all ASCII strings of length <= 1000 contains 1.622×10^2107 elements without problems.

$\endgroup$
3
  • $\begingroup$ "we only instantiate a single value," "that doesn't mean T only has one element." This sounds contradictory to me. How can have a type, at the same time, have 1 and more or less than 1 member? Also TypeScript, Scala and Racket all feature first-class singletons. Many languages also feature null, true and false as singletons (Kotlin, TS, PHP, Zig, D, C++, ...). Wrt tuple cardinality: I was mentioning the cardinality of a type's set of inhabitants - not an actual tuple type. $\endgroup$
    – Longinus
    Commented Jun 20, 2023 at 3:26
  • $\begingroup$ (eg. where int itself already is an n-tuple with n > 1; and where if "int" means "32bits," it is a 4294967296-tuple) $\endgroup$
    – Longinus
    Commented Jun 20, 2023 at 3:41
  • $\begingroup$ Ah, I thought you were referring to the singleton pattern. The types of singletons like null, true and false (if true and false have different types rather than bool being inhabited by 2 values) are isomorphic to the unit type. In the way you describe int32 as a 4294967296-tuple, the unit type is a 1-tuple, but types are generally described as sets of values, not as tuples of those values. $\endgroup$
    – Jasmijn
    Commented Jun 20, 2023 at 8:51
1
$\begingroup$

I think the main difference from a language design perspective, is that a unit type is a value type, whereas a singleton type (in OOP) is a reference type. See value types and reference types on Wikipedia.

A unit type has one value, but there might be many instances of that value (if they are represented in memory at all; in some implementations, unit types occupy no memory). Usually, there is no need to compare instances of a unit type by reference instead of by value, and particularly in functional languages it's not even possible to, but in some languages it is (Rust playground link):

#[derive(PartialEq, Eq)]
struct UnitType;

fn main() {
    let a = UnitType;
    let b = UnitType;
    
    println!("Equal by value: {}", a == b);
    // true
    
    println!("Equal by reference: {}", std::ptr::eq(&a, &b));
    // false
}

On the other hand, a singleton type has one instance, and there might be many references to that instance, but they are all equal by reference, not just by value.


There are some other differences in how unit types and singleton types are used in practice.

Generally, a unit type is used when some value must be represented but there is no information for that value to convey, e.g. the return value of a void function. Since a unit value is not meant to be used for anything, it has no methods or properties.

On the other hand, a singleton type in an object-oriented language exists to provide some functionality, so it typically does have methods and possibly also fields. It might implement some interfaces, and you might need to acquire a lock on the instance for some purposes. Access to the singleton instance might even be restricted in some way (e.g. sun.misc.Unsafe) because the singleton instance provides functionality which shouldn't be used everywhere.


what is this difference in cardinality?

This appears to be a confusion about types (which can be represented as a set of all members of the type) vs. values. To say that the unit type is like an empty tuple means that the unit value is an empty tuple; that is, Unit is a type whose only member is (). But this means that the set of all members of Unit is equal to {()}, i.e. a set containing the empty tuple. So the value is a tuple of length zero, whereas the type is represented by a set of cardinality one.

$\endgroup$
13
  • $\begingroup$ what languages have multiple instances of an unit type? your example doesn't work since you implemented it in userland, and has different semantics than usual compiler-backed unit types. $\endgroup$
    – RubenVerg
    Commented Jun 17, 2023 at 7:25
  • $\begingroup$ @RubenVerg I'm not sure why you think the example I gave in Rust "doesn't work" ─ the type is a unit type and it has two instances which are not equal by reference. This is not a peculiarity of user-defined types in Rust, the same works with the traditional empty tuple unit type. If that's not "compiler-backed" then I don't know what you are using that term to mean. $\endgroup$
    – kaya3
    Commented Jun 17, 2023 at 7:36
  • $\begingroup$ "Unit type" generally refers to the type that "void" functions return, not any type which is mathematically an unit type. $\endgroup$
    – RubenVerg
    Commented Jun 17, 2023 at 7:58
  • $\begingroup$ @RubenVerg Well, () is the type returned by void functions in Rust. But if you still object, then perhaps you will want to make edits to correct Wikipedia and the official Rust handbook, both of which consider () to be a unit type in Rust. Personally, I will continue to go by the plain definition that a unit type is one inhabited by a single value, and what I observe to be common usage. $\endgroup$
    – kaya3
    Commented Jun 17, 2023 at 8:02
  • 1
    $\begingroup$ @RubenVerg It's not an oddity at all; what's unusual about Rust is that you can acquire references to (). Otherwise you would never be able to observe that there can be different instances, as the concept of instances would not be relevant for the language's semantics ─ as I mentioned, this is true in functional languages, and it is a clear distinction between unit types and singleton classes. The distinction between "a" and "the" here does not matter for my answer since the question is about "unit types", not "the unit type" or "the designated unit type returned by void functions". $\endgroup$
    – kaya3
    Commented Jun 17, 2023 at 18:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .