Brief comment: Algebraic type is not a specific type, but a way of thinking about the original type. When the Sum type is used properly, it makes your code much more readable.

Algebraic types – what are they?

Algebraic types are not a new type. A way of thinking about the original type. There are many different types of algebra – in fact, all of the types you currently use are algebraic. Here we will introduce two basic types of algebra

  • Product type
  • The Sum type

So let’s start with something familiar. A Swift struct and a Java class are both products. They are called Product types because the number of possible values they have is a Product of the number of possible values for their components.

struct ProductExample { 
  let a: Bool 
  let b: Bool
}
Copy the code

So a Bool can have 2 possible values. ProductExample has two Bool values A and b. We can get the number of possible values for ProductExample by multiplying the number of possible values for A by the number of possible values for B. So the number of possible values for ProductExample is 2 x 2 = 4. All possible cases of this type are obvious:

let first = ProductExample(a: true, b: true)
let second = ProductExample(a: true, b: false)
let third = ProductExample(a: false, b: true)
let fourth = ProductExample(a: false, b: false)
Copy the code

Let’s look at another example:

struct ProductExampleTwo { 
  let a: Bool
  let b: Int8
}
Copy the code

Now our ProductExampleTwo type has the possible value, which is the product of Bool and Int8. Int8 has 256 possibilities, Bool has 2 possibilities 2. So our ProductExampleTwo has 512 possibilities. We can define a function Npv that returns the number of possible values of a type.

enum SumExample { 
  case a(Bool) 
  case b(Bool)}Copy the code

We don’t have to list the possible situations with SumExample

let first = SumExample.a(true)
let second = SumExample.b(true)
let third = SumExample.a(false)
let fourth = SumExample.b(false)
Copy the code

Npv(Bool)+Npv(Bool) = 2 + 2 = 4 Here’s another example

enum SumExampleTwo {
  case a(Bool) 
  case b(Int8)}Copy the code

Npv(SumExampleTwo)= Npv(Bool)+ Npv(Int8) = 2 + 256 = 258.

How can we write better code for this feature?

  1. Use enum as the return value: If we define a method to send a request that returns a String result, let’s look at the previous code.
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 }}Copy the code

Why is this a bad choice? Because our return value has two possible values:

  • Success – Gets results from the server
  • Error – Errors that occur during function processing

Looking at this code, we can make the following four judgments based on the return value:

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
Copy the code

But success and failure only require two possibilities: success :result! Error == nil failure: result == nil, error! The problem with = nil is that we are using the Product type instead of the Sum type. The code for changing the return value to enum now looks 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) 
    }
}
Copy the code

We create a Sum type called Result, which we use to distinguish between the two possibilities. Our use cases fit our reality, which is great.

  1. Optional enum: You may not know that Swift has a type – Optional. The internal implementation uses the Sum type enum
enum Optional<T> { 
      case some(T) 
      case none
}
Copy the code

So let a: String? = “Hello” is just a shorthand for let a = Optional. Some (“Hello”). The good news is that Swift has some neat syntactic sugar to help us distinguish between the Sum type-if let and guard let structures.

let a: String? = "Hello"
    if let a = a {
     print(a)
    } else {
     print("error")}Copy the code

Is equivalent to:

let a = Optional.some("Hello")
switch a {
    case .some(let res):
        print(res)
    case .none:
        print("Error")}Copy the code
  1. Use Sum to represent routes: The possibilities for something in your application are limited, and it’s very easy to represent it in Sum. For example, use an enum to represent the URL of a network request:
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)"}}}Copy the code

Your Router can use this to expose everything from parameters to headers to request types… Now, if you need to change your app theme style, try this:

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:
                return  AppThemeModel( 
                    baseColor: .white
                    backgroundColor: .gray 
                    accentColor: .blue 
                    baseFont: .systemFontOfSize(13) 
                  ) 
              }
          }
     }
// During app init
var currentAppTheme = AppTheme.dark
Copy the code
  1. Implementing data structures 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)))
Copy the code
enum Foo {
  case a
  case b
}
let sth = Foo.a
let oth = Foo.b
But can be also written like this:
enum Foo {
  case a(String)
  case b(isEnabled: Bool)
}
let sth = Foo.a("Hello")
let oth = Foo.b(isEnabled: false)
Copy the code

https://mislavjavor.github.io/2017-04-19/Swift-enums-are-sum-types.-That-makes-them-very-interesting/?utm_source=mybridg e&utm_medium=blog&utm_campaign=read_more