Study notes of serveral videos.
This video talks best practices in four areas: data, inheritance, funciton and type.
Step 1: Model with algebraic data types: product, sum and mixed types.
Step 2: Refine with smart constructors
case class Email(value: String), using a method that domain logic only works on valid domain data.
Step 3: embrace variance
Step 4: learn to love folds.
fold method to handle both cases.
Step 5: prefer type classes over interface. The interface doesn’t work in deserialization.
Step 6: make methods
abstract. Inheritance tangles types via
super.m() and it is hard to understand it.
Step 7: use type bound only for variance. As inheritance, type bounds couple types. Types are erased at runtime. No
Prism can help. Type class may help.
Step 8: Prefer values over expected errors. Use
Option/Try/Either/ZIO for possible failures. No error type:
Try; Any error (pure):
Either; Any error (effect):
Step 9: defer non-determinism. Move the non-determinism out of the local scope.
Step 10: describe, don’t do. Create a data structure to describe what you want to do. It is easy to test the data structure.
Step 11: Prefer global coherency. Implicit value is sensitive to location of the code. Defintion: for any type, at most one implicit that Scala can find. Better to put implicit into a type’s companion object.
Step 12: Peter one type per scope. For example:
do(true, false, true, false) could take wrong arguments. Use case class for each case. Instead of
transfer(from: Account, to: Account), use
transfer (receiver, sender). Sometimes use shadowing to hide outer variable.