Enum structure should be used by everyone. Let's take a brief look at the use of Swift.
The algebraic type is not a specific type, but a way of thinking about the original type. Sum type is a kind of algebraic type. Rational use of Sum type can greatly improve the readability of code.
Sum type
Enum in Swift is a typical Sum type, for example:enum SumExample { case a(Bool) case b(Bool) }
let first = SumExample.a(true) let second = SumExample.b(true) let third = SumExample.a(false) let fourth = SumExample.b(false)
It can be seen that the number of possible values of Enum is the sum of the number of possible values of all components of Enum, so the result of Npv(SumExample) is Npv(Bool) + Npv(Bool) = 2 + 2 = 4.
Another example is:
enum SumExampleTwo { case a(Bool) case b(Int8) }
Npv(SumExampleTwo) = Npv(Bool) + Npv(Int8) = 2 + 256 = 258.
How can we use this feature to write better code?
1. Use Enum as the return value:If we define a method to send a request and return a String type result, let's take a look at the code that was common before.
typealias Handler = (String?, Error?) -> Void func getUser(from: URL, completionHandler: Handler) { // function implementation } getUser(from: someUrl) { result, error in if let result = result { // Handle result } if let error = error { // Handle error } }
Why is this a bad choice? Because our return value has only two possibilities:
- success - Get results from the server
- fail-Function Processing Errors
result = nil, error = not nil // Case 1 result = not nil, error = nil // Case 2 result = not nil, error = not nil // Case 3 result = nil, error = nil // Case 4
But in fact, success or failure only requires two possibilities:
Success: result!= nil, error = nil
Failure: result = nil, error!= nil
The reason for this problem is that we use the Product type instead of the Sum type.
The code that replaces the return value with enum is now like this.
enum Result { case success(String) case error(Error) } typealias Handler = (Result) -> Void func getUser(from: URL, completionHandler: (Handler)) { // implementation } getUser(from: someUrl) { response in switch response { case .success(let result): print(result) case .error(let error): print(error.localizedDescription) } }
We created a Sum type called Result, which we used to distinguish between two possibilities. Our use case is in line with our actual situation, which is very good.
2. Optional enum
Optional, the most commonly used type of Swift, is implemented internally using the Sum type Enum:
enum Optional<T> { case some(T) case none }
So let a: String? = "Hello" is a short version of let a = Optional.some("Hello").
The good news is that Swift has some concise grammatical sugar to help us distinguish between Sum types - if let and guardlet structures.
let a: String? = "Hello" if let a = a { print(a) } else { print("error") }
Amount to:
let a = Optional.some("Hello") switch a { case .some(let res): print(res) case .none: print("Error") }
3. Use Sum type to represent routing
In your application, the possibilities for something are limited and can be easily expressed in Sum type. For example, enum is used to represent different network requests:
enum Router { case user(id: Int) case weather(day: Day) } extension Router { var url: String { switch self { case .user(let id): return "\(App.BaseUrl)/user/\(id)" case .weather(let day): return "\(App.BaseUrl)/weather/\(day.rawValue)" } } }
Your Router can expose everything in this way, such as parameters, request headers, request types, etc.
Now, if you replace the application theme style, you can try this way:
struct AppThemeModel { let baseColor: UIColor let backgroundColor: UIColor let accentColor: UIColor let baseFont: UIFont } enum AppTheme { case dark case light var model: AppThemeModel { switch self { case .dark: return AppThemeModel( baseColor: .red backgroundColor: .darkRed accentColor: .yellow baseFont: .systemFontOfSize(12) ) case .light: AppThemeModel( baseColor: .white backgroundColor: .gray accentColor: .blue baseFont: .systemFontOfSize(13) ) } } } // During app init var currentAppTheme = AppTheme.dark
4. Implementing data structure
It is very easy to implement trees and linked lists in swift using the sum type.
indirect enum Tree<T> { case node(T, l: Tree, r: Tree) case leaf(T) var l: Tree? { switch self { case .node(_, l: let l, _): return l case .leaf(_): return nil } } var r: // equivalent implementation to l var value: T { switch self { case .node(let val, _, _): return val case .leaf(let val): return val } } } let tree = Tree.node(12, l: Tree.leaf(11), r: Tree.node(34, l: Tree.leaf(34), r: Tree.leaf(55)))