$30 off During Our Annual Pro Sale. View Details »

Building Robust Apps (Swift Edition)

Building Robust Apps (Swift Edition)

Ragunath Jawahar

October 10, 2020
Tweet

More Decks by Ragunath Jawahar

Other Decks in Programming

Transcript

  1. Ragunath Jawahar Building Robust Software Small ideas, big impact

  2. Write code that communicates well.

  3. Example 1 API Call / External Systems

  4. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  5. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  6. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  7. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  8. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  9. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  10. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  11. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  12. Response? • Successfully creates a user account and returns a

    Response 🎉 • nil • NSURLErrorCannotConnectToHost • NSURLErrorCannotFindHost • NSURLError* "
  13. Response? • Successfully creates a user account and returns a

    Response 🎉 • nil • NSURLErrorCannotConnectToHost • NSURLErrorCannotFindHost • NSURLError* "
  14. Response? • Successfully creates a user account and returns a

    Response 🎉 • nil • NSURLErrorCannotConnectToHost • NSURLErrorCannotFindHost • NSURLError* "
  15. Response? • Successfully creates a user account and returns a

    Response 🎉 • nil • NSURLErrorCannotConnectToHost • NSURLErrorCannotFindHost • NSURLError* "
  16. Response? • Successfully creates a user account and returns a

    Response 🎉 • nil • NSURLErrorCannotConnectToHost • NSURLErrorCannotFindHost • NSURLError* "
  17. 😊 😕 😨 Caller’s emotions

  18. 😊 😕 😨 Caller’s emotions

  19. 😊 😕 😨 Caller’s emotions

  20. 😊 😕 😨 Caller’s emotions

  21. Be conservative in what you send, be liberal in what

    you accept Robustness Principle / Postel’s Law
  22. Be conservative in what you send, be liberal in what

    you accept Robustness Principle / Postel’s Law
  23. Robustness Principle / Postel’s Law Be conservative in what you

    send, be liberal in what you accept
  24. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  25. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response? } AccountsApi.swift
  26. protocol AccountsApi { func signup(with email: String, and password: String)

    #-> Response } AccountsApi.swift
  27. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  28. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  29. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  30. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  31. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  32. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  33. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  34. Response • Successfully creates a user account • Email already

    registered • Validation errors • Connection errors • 5xx server errors • Unknown errors
  35. Enumerations An enumeration defines a common type for a group

    of related values and enables you to work with those values in a type-safe way within your code. Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages.
  36. Enumerations An enumeration defines a common type for a group

    of related values and enables you to work with those values in a type-safe way within your code. Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages.
  37. Enumerations An enumeration defines a common type for a group

    of related values and enables you to work with those values in a type-safe way within your code. Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages.
  38. Enumerations An enumeration defines a common type for a group

    of related values and enables you to work with those values in a type-safe way within your code. Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages.
  39. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  40. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  41. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  42. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  43. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  44. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  45. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  46. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  47. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  48. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  49. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  50. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  51. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  52. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  53. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  54. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  55. Usage - Enum Exhaustiveness val response = accountsApi.signup(with: "John Doe",

    and: "john@example.org") switch response { case .accountCreated(let userId): saveUserId(userId) case .emailAlreadyRegistered(let registrationEmail): showEmailAlreadyRegistered(registrationEmail) case .inputValidationFailed(let errors): showValidationErrors(errors) case .connectionError: showCheckConnectionMessage() case .serverError(_), .unknownError(_, _): showTryAgainInSometimeMessage() }
  56. 😊 😌 Caller’s emotions

  57. 😊 😌 Caller’s emotions

  58. 😊 😌 Caller’s emotions

  59. What have we achieved? • Certainty • Convey business ideas

    in code • Improved readability • Represent errors as data
  60. What have we learnt? • Total functions • Algebraic data

    types • Robustness principle
  61. What have we learnt? • Total functions • Algebraic data

    types • Robustness principle
  62. Total Functions Total functions are functions that give you a

    valid return value for every combination of valid arguments.
  63. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  64. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  65. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  66. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  67. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  68. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  69. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  70. Partial Functions A partial function is a function that is

    not defined for all possible input values; in some cases returns a value, it may never return at all, throw an exception or simply crash a system.
  71. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  72. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  73. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  74. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  75. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  76. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  77. !// Partial function func signup(with email: String, and password: String)

    #-> Response? !// Total function func signup(with email: String, and password: String) #-> Response Partial vs. Total Functions
  78. What have we learnt? • Total functions • Algebraic data

    types • Robustness principle
  79. What have we learnt? • Total functions • Algebraic data

    types • Robustness principle
  80. Algebraic Data Types An algebraic data type is a kind

    of composite type, i.e., a type formed by combining other types.
  81. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  82. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  83. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  84. Response.swift enum Response { case accountCreated(String) case emailAlreadyRegistered(String) case InputValidationFailed([ValidationError])

    case connectionError case serverError(Int) case unknownError(Int, String?) }
  85. What have we learnt? • Total functions • Algebraic data

    types • Robustness principle
  86. What have we learnt? • Total functions • Algebraic data

    types • Robustness principle
  87. Where to apply? • Use it heavily on integration points

    with external systems (code that lives outside your own application) • Anti-corruption layers (both internal and external) • Take failure frequency into account (databases and file systems may not be good candidates. Network layer is a good starting point.)
  88. Example 2 Division / Exceptions

  89. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  90. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  91. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  92. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  93. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  94. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  95. func divide(dividend: Int, divisor: Int) #-> Int { return dividend

    / divisor } Arithmetic.swift
  96. divisor = 0

  97. ERROR : divide by zero error: Execution was interrupted, reason:

    EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). Fatal error: Division by zero: file Swift/x86_64-apple-ios- simulator.swiftinterface, line 32420
  98. Be conservative in what you send, be liberal in what

    you accept
  99. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  100. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  101. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  102. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  103. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  104. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  105. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  106. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  107. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  108. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  109. func divide(dividend: Int, divisor: Int) #-> Quotient { return divisor

    #== 0 ? .infinity : .result(dividend / divisor) } Arithmetic.swift
  110. enum Quotient { case .infinity case .result(Int) } Quotient.swift

  111. enum Quotient { case .infinity case .result(Int) } Quotient.swift

  112. enum Quotient { case .infinity case .result(Int) } Quotient.swift

  113. enum Quotient { case .infinity case .result(Int) } Quotient.swift

  114. enum Quotient { case .infinity case .result(Int) } Quotient.swift

  115. 🤔

  116. Implications • Places burden on the consumer to handle both

    Infinity and Result types • The consumer may pass the Quotient type downstream. If that happens, downstream consumers should also deal with the added complexity
  117. Be conservative in what you send, be liberal in what

    you accept
  118. Be conservative in what you send, be liberal in what

    you accept conservative
  119. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  120. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  121. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  122. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  123. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  124. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  125. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  126. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  127. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  128. func divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int { return dividend

    / divisor.value } Arithmetic.swift
  129. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  130. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  131. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  132. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  133. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  134. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  135. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  136. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  137. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  138. class NaturalNumber { class Zero: NaturalNumber { !/* empty !*/

    } class NonZero: NaturalNumber { let value: Int fileprivate init(_ value: Int) { self.value = value } } !//!!... } NaturalNumber.swift
  139. class NaturalNumber { !//!!... class func from(_ value: Int) #->

    NaturalNumber { return value #== 0 ? Zero() : NonZero(value) } } NaturalNumber.swift
  140. class NaturalNumber { !//!!... class func from(_ value: Int) #->

    NaturalNumber { return value #== 0 ? Zero() : NonZero(value) } } NaturalNumber.swift
  141. class NaturalNumber { !//!!... class func from(_ value: Int) #->

    NaturalNumber { return value #== 0 ? Zero() : NonZero(value) } } NaturalNumber.swift
  142. class NaturalNumber { !//!!... class func from(_ value: Int) #->

    NaturalNumber { return value #== 0 ? Zero() : NonZero(value) } } NaturalNumber.swift
  143. class NaturalNumber { !//!!... class func from(_ value: Int) #->

    NaturalNumber { return value #== 0 ? Zero() : NonZero(value) } } NaturalNumber.swift
  144. class NaturalNumber { !//!!... class func from(_ value: Int) #->

    NaturalNumber { return value #== 0 ? Zero() : NonZero(value) } } NaturalNumber.swift
  145. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  146. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  147. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  148. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  149. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  150. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  151. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  152. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  153. let divisor = NaturalNumber.from(4) if (divisor is NaturalNumber.NonZero) { val

    quotient = divide(divisor: 8, divisor: divisor) !// Do fancy things! } else { !// Fail fast… } Usage
  154. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  155. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  156. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  157. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  158. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  159. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  160. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  161. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  162. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  163. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  164. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  165. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  166. !// Liberal inputs, conservative output (original) fun divide(dividend: Int, divisor:

    Int) #-> Quotient !// Conservative inputs, conservative output (tweaked) fun divide(dividend: Int, divisor: NaturalNumber.NonZero) #-> Int Original vs. Tweaked
  167. What have we learnt? • Robustness principle (tweaked)

  168. Where to apply? • All layers and components that are

    within your system boundary
  169. Impact on Overall System Design • Enhances certainty on micro

    and macroscopic levels • Errors are treated as first-class citizens • Reduced range of motion for your data from its point of origin • Easy to move towards a functional core and an imperative shell style of architectural pattern • Focus moves towards data over actors • Promotes value boundaries over call boundaries • Effective cure against primitive obsession • Reduces defensive programming • Promotes property testing over behaviour testing
  170. Summary • Robustness Principle • Total Functions • Algebraic Data

    Types • Robustness Principle (Tweaked)
  171. Questions? @ragunathjawahar