Swift — Literals by Tutorials
ExpressibleBy protocol | Use of Literals | Extend custom types
Updated on 18th Dec 2022, 03:40 PM GMT 5:30+
- What is literal?
- How Swift is managing literals and inferring specific types?
- How we can define custom types with Literals?
- Bonus topic — How we can automatically initialise Bool variables using Int values?
- Limitations of ExpressibleByXXXLiteral protocols of Swift Standard library
What is literal?
A literal is any value used in code. e.g. String value, Int value or Array value. Literals in Swift specify value only, not a specific datatype.
Examples of Literals,
1708
"Hello Reader.!"
[1, 2, 3, 5, 8 , 13]
1708 is Integer Literal , “Hello Reader.!” is String Literal, [1, 2, 3, 5, 8 , 13] is Array Literal
Use of Literals in Swift Code
let intValue= 1708
let stringValue = "Hello Reader.!"
let arrayValue = [1, 2, 3, 5, 8 , 13]
How Swift is managing literals and inferring specific types?
Swift standard library provide different protocols to manage literals in swift. Swift is managing literals in three different categories such as Value Literals, String Literals and Collection Literals.
┌─────────────────────────────────────────────┬───────────────────────────────┬─────────────────────┐
│ Swift Protocol │ Literal Value │ Literal Category │
├─────────────────────────────────────────────┼───────────────────────────────┼─────────────────────┤
│ ExpressibleByIntegerLiteral │ 1708, 999, -786 │ Value Literals │
│ ExpressibleByFloatLiteral │ 1.7, 3.14 │ Value Literals │
│ ExpressibleByBooleanLiteral │ true, false │ Value Literals │
│ ExpressibleByNilLiteral │ nil │ Value Literals │
│ ExpressibleByStringLiteral │ "Hello", "Login" │ String Literals │
│ ExpressibleByExtendedGraphemeClusterLiteral │ "A","🇮🇳" │ String Literals │
│ ExpressibleByUnicodeScalarLiteral │ "A", "\u{1F1EE}" │ String Literals │
│ ExpressibleByArrayLiteral │ [1,2] , ["A","An"] │ Collection Literals │
│ ExpressibleByDictionaryLiteral │ ["id": 1, "name": "Hitendra"] │ Collection Literals │
└─────────────────────────────────────────────┴───────────────────────────────┴─────────────────────┘
Have you ever thought why assignment of literal to variable is possible without providing type in Swift? consider below example.
//Code block 1.
//Swift will automatically inferred type as Int
let variable1 = 123
//Code block 2.
//Swift will automatically inferred type as Double
let variable2 = 3.14
Int
type in Swift confirms to ExpressibleByIntegerLiteral.
type in Swift confirms to
DoubleExpressibleByFloatLiteral & ExpressibleByIntegerLiteral
type in Swift confirms to
FloatExpressibleByFloatLiteral & ExpressibleByIntegerLiteral
When swift compiler find any literal in code, it try to infer the type automatically. It does this by checking all the types those confirms protocols ExpressibleByXXXLiteral.
In above example,
Code block 1,
value 123 is considered intLiteral in Swift. Int, Double, Float confirms to protocol ExpressibleByIntegerLiteral, so there are three types possible for intLiterals which are Int, Double, Float.
Swift consider Int type as first priority for intLiterals so compiler will inferred Int as default type for intLiterals. That’s why variable1 in above example will be inferred as Int type automatically.
Code bock 2,
value 3.14 is considered floatLiteral in Swift. Double and Float both confirms to protocol ExpressibleByFloatLiteral, so there are two types possible for floatLiterals which are Double and Float.
Swift consider Double as first priority for floatLiterals so it will inferred Double as default type for floatLiterals. That’s why variable2 in above example will be inferred as Double type automatically.
How we can define custom types with Literals?
Let’s assume that we are implement LinkedList for Int data. We can also store any type of data using generics but it’s not a topic of this blog post. For simplicity and easy to understand, we are just focusing on Int data for LinkedList.
class LinkedList {
//properties required for storing head, tail, etc...
init() {
//logic to create empty Linked list without nodes
}
init(intArray: [Int]) {
//logic to create nodes and setting up pointers for next nodes
}
}
let linkedList1: LinkedList = LinkedList(intArray: [3,5,8])
In above example, we have linkedList1 with initial data. If you observe closely, we have to call init(intArray:) in order to create instance of LinkedList.
Can we use arrayLiteral directly to create instance of LinkedList?
Yes, this is the best opportunity for us to use ExpressibleByArrayLiteral protocol to achieve this.
class LinkedList: ExpressibleByArrayLiteral {
//properties required for storing head, tail, etc...
init() {
//logic to create empty Linked list without nodes
}
init(intArray: [Int]) {
//logic to create nodes and setting up pointers for next nodes
}
//requirements of protocol ExpressibleByArrayLiteral
typealias ArrayLiteralElement = Int
required convenience init(arrayLiteral elements: LinkedList.ArrayLiteralElement...) {
self.init(intArray: elements)
}
}
let linkedList1: LinkedList = LinkedList(intArray: [3, 5, 8]) //using int(intArray:)
let linkedList2: LinkedList = [10, 20, 30] //using arrayLiteral
In above example, we confirmed ExpressibleByArrayLiteral protocol to our custom type LinkedList. So we can directly assign arrayLiterals(array value) to LinkedList type variable. We have created variable linkedList2 by just using arrayLiteral(value = [10, 20, 30]).
Bonus topic.
We can also extend Swift standard library types for our convenience. How we can automatically initialise Bool variables using Int values?
Bool
type in Swift confirms to ExpressibleByBooleanLiteral.
//Code block 1.
//Swift will automatically inferred type as Bool
let variable1 = true
//Code block 2.
//Trying to make boolean using Int value, not possible directly
let variable2 = 1
//Code block 3.
//Trying to make boolean using Int value with explacitly type declaration
//Swift Compiler Error: Type 'Int' cannot be used as a boolean;
let variable3: Bool = 1
In above example,
Code block 1.
variable1 of type Bool is declared just by using boolLiteral(true) and swift will inferred type as Bool for the variable1
Code block 2.
In other programming languages 1/0 are logical boolean, so here we are trying to declare variable2 of type Bool. In Swift 1/0 are considered as intLiterals so swift will inferred type of variable2 as Int and not Bool.
Code block 3.
Swift consider 1/0 as intLiteral and automatic infer Int type for the declared variable, so we are trying to specify Bool type here explicitly. In this case swift compiler will show error as below.
Why swift shows this compilation time error?
Swift will try to convert Int type to Bool type, but swift standard library don’t have any logic for Int type to Bool type conversion.
How we can solve this error?
We can provide Int type to Bool type conversion logic to Swift. For this, We needs to extend Bool type by confirming ExpressibleByIntegerLiteral protocol as below example.
extension Bool: ExpressibleByIntegerLiteral {
public typealias IntegerLiteralType = Int
public init(integerLiteral value: Int) {
//using if-else condition here, so everyone can understand easily
if value <= 0 {
self = false
} else {
self = true
}
}
}
let variable3: Bool = 1 //true
let variable4: Bool = 5//true
let variable5: Bool = 0//false
let variable4: Bool = -9 //false
In above example, we have added logic to convert Int to Bool for Boolean type provided by swift. As per above logic we are considering any positive value as true and any negative or zero value as false.
While declaring variables we have to define the type explicitly otherwise compiler will again consider intLiteral(val) as Int type by default.
Limitations - of ExpressibleByXXXLiteral protocols of Swift Standard library
- We can not constraint only specific group of values when implementing any ExpressibleByXXXLiteral protocol.
- We can not write fail-able initialisers.
- We cannot limit 1 and 0 only when confirming ExpressibleByIntegerLiteral protocol to Bool type, so we needs to write logic for all possible literals for the protocol ExpressibleByIntegerLiteral.
Learning from this blog post.
Consider this opportunity and extend Swift type or your custom type with ExpressibleByXXXLiteral protocol for easy initialisation without need of calling init() explicitly during declaration of variable.
Liked this article? Give claps and show your support.
It doesn’t cost you anything to clap.
Write a response, if you find any scope of improvements or learnt something new today by reading this blog post.