So as we all know, in Swift to declare a class, you say
let variable = ClassName(parameters)
however, to declare things like Arrays and integer, you can just say
let variable = 32
(Instead of Int(32))
let variable = [3,2,4]
(instead of let variable = Array<Int>(3,2,4)
Let's say I want to create a class for complex (imaginary numbers) that can be declared like this:
let complexNumber = 5i
or
let complexNumber = 3i
Note that the complex numbers aren't necessarily the focus of the question, and the making your own unboxable is
CodePudding user response:
Swift Literals
In Swift, these syntaxes you described are called "literals", which are a convenient way for initializing values of types which conform to a particular set of protocols. The syntaxes of literals is not user-extensible, because it relies on the Swift parser to understand how to parse them. The supported ones are:
Literal | Protocol |
---|---|
Integer | ExpressibleByIntegerLiteral |
Floating-Point | ExpressibleByFloatLiteral |
String | ExpressibleByStringLiteral |
Extended Grapheme Cluster | ExpressibleByExtendedGraphemeClusterLiteral |
Unicode Scalar | ExpressibleByUnicodeScalarLiteral |
Boolean | ExpressibleByBooleanLiteral |
Nil | ExpressibleByNilLiteral |
Array | ExpressibleByArrayLiteral |
Dictionary | ExpressibleByDictionaryLiteral |
(See NSHipster's article about literals for more details).
But I get your point more broadly: You want to express syntax from another domain (e.g. math) in a similar way within Swift code, so it's familiar to subject-matter experts from that domain.
There are plenty of workarounds, depending on the particular problem.
For your complex number example, could do is define a new Complex
type, with a *
operator and i
value, which lets you write:
let complexNumber = 5 * i
There's plenty of numeric programming libraries (notably the first-party swift-numerics, that define such types (though not necessarily the same syntax).
You can also see what Apple did with SwiftUI, using result-builders to build a Swift DSL for eloquently describing view hierarchies.
Bonus: It's not "boxing"
There's no boxing going on here. I suspect you're from a Java background, where boxing is the process of allocating objects to store primitive types, because only then can they be passed around into objects (such as to be stored into containers like ArrayList
).
This isn't the case in Swift. There are no "primitive" types. There are reference types (instances of classes and class-bound protocol existentials), and value types (structs, enums and protocol existentials). Swift's reified generics can handle values and reference objects alike, e.g. Int
can go right into an Array<Int>
. So most of the time, there's no boxing to be done.
There are places where Swift runs into the same issue that makes Java needs to box values: contiguous storage like an array can't handle having elements be different sizes, so code like this becomes a challenge:
let x: Array<Any> = [true, "abc", 123, someObject]
Further, there needs to be some way to express how values of different types are handled (retained, release, copied, etc.).
To solve these problems, the compiler will automatically generate code to box these values into boxes call "existential containers", which store the value, and a table of function pointers that describe the operations that can be performed on this value. While the sizes of the values are different, all existential containers have the same size, so you can create an array out of them.