Are There Any Possible Explicit Uses of Instances (Values) of Empty Tuples (), I.E., of Instances of Typealias 'Void'

Are there any possible explicit uses of instances (values) of empty tuples (), i.e., of instances of typealias 'Void'?

There are lots of places that () can be useful when playing around with "CS" problems, which often have the form "implement X using Y even though you really already have X." So for instance, I might say, implement Set using Dictionary. Well, a Dictionary is a Key/Value pair. What should the type of the Value be? I've actually seen this done in languages that have Dictionaries but not Sets, and people often use 1 or true as the value. But that's not really what you mean. That opens up ambiguity. What if the value is false? Is it in the set or not? The right way to implement Set in terms of Dictionary is as [Key: ()], and then you wind up with lines of code like:

set[key] = ()

There are other, equivalent versions, like your Optional<()>. I could also implement integers as [()] or Set<()>. It's a bit silly, but I've done things like that to explore number theory before.

That said, these are all almost intentionally impractical solutions. How about a practical one? Those usually show up when in generic programming. For example, imagine a function with this kind of form:

func doThingAndReturn<T>(retval: T, f: () -> Void) -> T {
f()
return retval
}

This isn't as silly as it sounds. Something along these lines could easily show up in a Command pattern. But what if there's no retval; I don't care about the return? Well, that's fine, just pass a () value.

func doThing(f: () -> Void) {
doThingAndReturn((), f: f)
}

Similarly, you might want a function like zipMap:

func zipMap<T, U>(funcs: [(T) -> U], vals: [T]) -> [U] {
return zip(funcs, vals).map { $0($1) }
}

This applies a series of functions that take T to values of type T. We could use that even if T happens to (), but we'd have to generate a bunch of () values to make that work. For example:

func gen<T>(funcs: [() -> T]) -> [T] {
return zipMap(funcs, vals: Array(count: funcs.count, repeatedValue: ()))
}

I wouldn't expect this to come up very often in Swift because Swift is mostly an imperative language and hides its Void in almost all cases. But you really do see things like this show up in functional languages like Scala when they bridge over into imperative programming.

What is type '()' in swift?

() is both a type (and Void is a type alias for the empty tuple type) and the only value of that type. So

let x = ()

defines x as a constant property of type () (aka Void) with the value ().

Equivalent statements would be

let x:() = ()
let x:Void = ()

but not

let x = Void // error: expected member name or constructor call after type name

because Void is a type but not a value.

When defining function types with no parameters and/or no return value
there seems to be a consensus to use () for an empty parameter list,
and Void for no return value. This can for example be seen in
UIKit completion handlers, or for execution blocks submitted to
GCD dispatch queues, e.g.

func sync(execute block: () -> Void)

What is return type () ?

Your function iterateItems has removeItem addItem calculateEfficiency parameter is function type. So you must use function name as params when call function.

like this:

iterateItems(itemArray: WeaponItems.weaponItems, removeItem: removeWeaponItem, addItem: addWeaponItem, calculateEfficiency: calcWeaponDamage)

Are there any possible explicit uses of instances (values) of empty tuples (), i.e., of instances of typealias 'Void'?

There are lots of places that () can be useful when playing around with "CS" problems, which often have the form "implement X using Y even though you really already have X." So for instance, I might say, implement Set using Dictionary. Well, a Dictionary is a Key/Value pair. What should the type of the Value be? I've actually seen this done in languages that have Dictionaries but not Sets, and people often use 1 or true as the value. But that's not really what you mean. That opens up ambiguity. What if the value is false? Is it in the set or not? The right way to implement Set in terms of Dictionary is as [Key: ()], and then you wind up with lines of code like:

set[key] = ()

There are other, equivalent versions, like your Optional<()>. I could also implement integers as [()] or Set<()>. It's a bit silly, but I've done things like that to explore number theory before.

That said, these are all almost intentionally impractical solutions. How about a practical one? Those usually show up when in generic programming. For example, imagine a function with this kind of form:

func doThingAndReturn<T>(retval: T, f: () -> Void) -> T {
f()
return retval
}

This isn't as silly as it sounds. Something along these lines could easily show up in a Command pattern. But what if there's no retval; I don't care about the return? Well, that's fine, just pass a () value.

func doThing(f: () -> Void) {
doThingAndReturn((), f: f)
}

Similarly, you might want a function like zipMap:

func zipMap<T, U>(funcs: [(T) -> U], vals: [T]) -> [U] {
return zip(funcs, vals).map { $0($1) }
}

This applies a series of functions that take T to values of type T. We could use that even if T happens to (), but we'd have to generate a bunch of () values to make that work. For example:

func gen<T>(funcs: [() -> T]) -> [T] {
return zipMap(funcs, vals: Array(count: funcs.count, repeatedValue: ()))
}

I wouldn't expect this to come up very often in Swift because Swift is mostly an imperative language and hides its Void in almost all cases. But you really do see things like this show up in functional languages like Scala when they bridge over into imperative programming.

Variables of type void

Note: This is a crosspost of my reply on the cross-posted question in the Apple Dev Forums.

It's because Swift seems to be a little academically favourable (it's one of Swift's plus points for me! :-) ).

A mathematical function is a relation between some inputs (the arguments) and some outputs (the return values). Instead of defining a whole class (e.g. "subroutine" or "procedure") for which this doesn't apply (no output, specifically), Swift simply defines a "procedure" as a relation with some set of inputs (possibly empty) and an empty set of outputs.

There are some benefits to it, though, albeit uncommon (at least until functional programming becomes the more dominant style in the language and the community). A little example:

infix operator ~~ { precedence 10 associativity right }
func ~~(l: A -> B, r: A) -> B {
return l(r)
}

let productsSoldIn2014 = productOfSales ~~ salesInYears ~~ [2014]
let salespersonsInvolvedIn2014Sales = salespersonsOfSales ~~ salesInYears ~~ [2014]

fireSalespersons ~~ salespersonsOfSales ~~ salesInYears ~~ [2014] // Awful companies only.

func salesInYears(_ y: [Int]) -> [Sale] { … }
func productsOfSales(_ s: [Sale]) -> [Product] { … }
func salespersonsOfSales(_ s: [Sale]) -> [SalesPerson] { … }
func fireSalespersons(_ s: [SalesPerson]) -> () { … } // The appended arrow and null tuple can be left out.

I've defined a "chaining" operator ~~ (think of a bending chain) which, right to left, pushes a value through the chain. With some appropriately named functions, we can declaratively define the values we need. (With lazy collections instead of arrays, we approach Haskell paradigms. Unless I'm mistaken, ~~ is here an identity monad. Or it's just a composition operator.)

The third statement (after the two lets) fires the salespersons that sold anything in the year 2014. Note though that ~~ wants to return "fireSalespersons"'s value (to continue the chain on the left, which here isn't present), which in Swift is valid—it's just the null tuple and we discard it. There's no reason why functions without return value shouldn't work with the operator (without overloading it).

By the way, this is also valid:

increaseCEOBonus(35000) ~~ fireSalespersons ~~ salespersonsOfSales ~~ salesInYears ~~ [2014]      // Really awful companies only.
// () -> () ~~ [Salesperson] -> () ~~ [Sales] -> [Salesperson] …

func increaseCEOBonus(amount: Int)() { … }

In this example, the leftmost chain is value-less—it's still left after right, but there aren't any values passed to the left. I wouldn't recommend this use though, it's better to simply enumerate each of them on a separate line.

To return to your question, since "nothing" can be consumed (via arguments), it'd be silly to explicitly prohibit "nothing" to be assigned to a variable—arguments are variables.

P.S.: Before anybody asks: yes, I did want to have an excuse to make a contrived example; it was just to test my ability to write complex types.

Swift running sum

The general combinator you're looking for is often called scan, and can be defined (like all higher-order functions on lists) in terms of reduce:

extension Array {
func scan<T>(initial: T, _ f: (T, Element) -> T) -> [T] {
return self.reduce([initial], combine: { (listSoFar: [T], next: Element) -> [T] in
// because we seeded it with a non-empty
// list, it's easy to prove inductively
// that this unwrapping can't fail
let lastElement = listSoFar.last!
return listSoFar + [f(lastElement, next)]
})
}
}

(But I would suggest that that's not a very good implementation.)

This is a very useful general function, and it's a shame that it's not included in the standard library.

You can then generate your cumulative sum by specializing the starting value and operation:

let cumSum = els.scan(0, +)

And you can omit the zero-length case rather simply:

let cumSumTail = els.scan(0, +).dropFirst()

Return in function without return value in Swift

The following signatures all return an instance of the empty tuple () (typealiased as Void).

func implicitlyReturnEmptyTuple() { }

func explicitlyReturnEmptyTuple() {
return ()
}

func explicitlyReturnEmptyTupleAlt() {
return
}

In all of the three above, the return type of the function, in the function signature, has been omitted, in which case it is implicitly set to the empty tuple type, (). I.e., the following are analogous to the three above

func implicitlyReturnEmptyTuple() -> () { }

func explicitlyReturnEmptyTuple() -> () {
return ()
}

func explicitlyReturnEmptyTupleAlt() -> () {
return
}

With regard to your comment below (regarding body of implicitlyReturnEmptyTuple() where we don't explicitly return ()); from the Language Guide - Functions: Functions without return values:

Functions are not required to define a return type. Here’s a version
of the sayHello(_:) function, called sayGoodbye(_:), which prints
its own String value rather than returning it:

func sayGoodbye(personName: String) {
print("Goodbye, \(personName)!")
}

...

Note

Strictly speaking, the sayGoodbye(_:) function does still return a
value
, even though no return value is defined. Functions without a
defined return type return a special value of type Void
. This is
simply an empty tuple, in effect a tuple with zero elements, which can
be written as ().

Hence, we may omit return ... only for ()-return (Void-return) function, in which case a () instance will be returned implicitly.

Difference between returning Void and () in a swift closure

If you look into headers, you will see that Void is typealias for (),

/// The empty tuple type.
///
/// This is the default return type of functions for which no explicit
/// return type is specified.
typealias Void = ()

You can verify that using playground like this,

let a  = Void()
print(a) // prints ()

Update

If you wish to see the declaration of Void in header file, in the above code, type the code below in swift playground or xcode editor like so,

  let a: Void  =  ()

Then, cmd + click on the "Void" keyword in above expression, you will be taken to the header file where you can actually see the declaration for Void.

The document has been updated with more information which is like this,

/// The return type of functions that don't explicitly specify a return type;
/// an empty tuple (i.e., `()`).
///
/// When declaring a function or method, you don't need to specify a return
/// type if no value will be returned. However, the type of a function,
/// method, or closure always includes a return type, which is `Void` if
/// otherwise unspecified.
///
/// Use `Void` or an empty tuple as the return type when declaring a
/// closure, function, or method that doesn't return a value.
///
/// // No return type declared:
/// func logMessage(_ s: String) {
/// print("Message: \(s)")
/// }
///
/// let logger: (String) -> Void = logMessage
/// logger("This is a void function")
/// // Prints "Message: This is a void function"
public typealias Void = ()

Get Type Argument of Arbitrarily High Generic Parent Class at Runtime

The following approach is based on __class_getitem__ and __init_subclass__. It might serve your use case, but it has some severe limitations (see below), so use at your own judgement.

from __future__ import annotations

from typing import Generic, Sequence, TypeVar


T = TypeVar('T')


NO_ARG = object()


class Parent(Generic[T]):
arg = NO_ARG # using `arg` to store the current type argument

def __class_getitem__(cls, key):
if cls.arg is NO_ARG or cls.arg is T:
cls.arg = key
else:
try:
cls.arg = cls.arg[key]
except TypeError:
cls.arg = key
return super().__class_getitem__(key)

def __init_subclass__(cls):
if Parent.arg is not NO_ARG:
cls.arg, Parent.arg = Parent.arg, NO_ARG


class Child1(Parent[int]):
pass


class Child2(Child1):
pass


class Child3(Parent[T], Generic[T]):
pass


class Child4(Parent[Sequence[T]], Generic[T]):
pass


def get_parent_type_parameter(cls):
return cls.arg


classes = [
Parent[str],
Child1,
Child2,
Child3[int],
Child4[float],
]
for cls in classes:
print(cls, get_parent_type_parameter(cls))

Which outputs the following:

__main__.Parent[str] <class 'str'>
<class '__main__.Child1'> <class 'int'>
<class '__main__.Child2'> <class 'int'>
__main__.Child3[int] <class 'int'>
__main__.Child4[float] typing.Sequence[float]

This approach requires that every Parent[...] (i.e. __class_getitem__) is followed by an __init_subclass__ because otherwise the former information may be overwritten by a second Parent[...]. For that reasons it won't work with type aliases for example. Consider the following:

classes = [
Parent[str],
Parent[int],
Parent[float],
]
for cls in classes:
print(cls, get_parent_type_parameter(cls))

which outputs:

__main__.Parent[str] <class 'float'>
__main__.Parent[int] <class 'float'>
__main__.Parent[float] <class 'float'>


Related Topics



Leave a reply



Submit