2. Motivation / Idea
● I’m a fan of functional programming and Domain Driven Design
● used Haskell, Elm, and saw something valuable there
● But never managed to convert those ideas to business reality properly
● Hard to find material about it, rather you are a super expert or knows
nothing at all
4. Motivation / Idea
● Mypy bring this concepts to something more palpable than haskell
● The concepts are replicable to other languages, open-api, etc
● Fits as a glove with event based distributed systems
● Assumes some DDD and FP knowledge
6. What is a type?
Set of possible value that can be used as inputs or outputs of a function
7. What is a type system?
Formalize and enforce the otherwise implicit categories (types) the programmer uses.
The main purpose of a type system is to reduce possibilities for bugs in computer
programs.
Type systems have other purposes as well, such as expressing business rules, enabling
certain compiler optimizations, providing a form of documentation, etc.
8. What is mypy?
Mypy is a static type checker for Python 3 and Python 2.7.
A tool that implements pep 484, types are optional
No runtime validation/overhead
Used by Google and Dropbox
Facebook have their own versions of it
9. Building blocks of the type-system algebra
Using my startup idea to kill goodreads as example (okreads).
10. Simple values - New type
People don’t think in term of
integers and string. They talk
OrderId, customer name, etc
Isbn10 = NewType('Isbn10', str)
Isbn5 = NewType('Isbn5', str)
11. Simple values - New type
def download_book_by_isbn(isbn: Isbn10):
...
isbn_string = "978-3-16-148410-0"
isbn_type = Isbn10(isbn_string)
download_book_by_isbn(isbn_type)
download_book_by_isbn(isbn_string)
mypy test.py
test.py:14: error: Argument 1 to "download_book_by_isbn" has
incompatible type "str"; expected "Isbn10"
Found 1 error in 1 file (checked 1 source file)
12. Combinations of values AND
IsbnInfo = NamedTuple('IsbnInfo', [('isbn5', Optional[Isbn5]),
('isbn10', Optional[Isbn10])])
14. Modelling choices OR
Also good for modeling
different states in the
domain. Types can also
represent certainty.
AIsbn = Union[Isbn5, Isbn10]
15. Modelling choices OR
Also good for modeling
different states in the
domain
UnvalidatedBookData = NamedTuple('UnvalidatedBookData', [('author_data', Any), ('title', str)])
class ValidatedBookData:
def __init__(self, data: UnvalidatedBookData):
if len(data.title) < 3:
raise Exception('Title should be bigger than 3 chars')
self.title = data.title
self.author_data = data.author_data
PersistedBookData = NewType('PersistedBookData', 'ValidatedBookData')
BookState = Union[UnvalidatedBookData, ValidatedBookData, PersistedBookData]
17. Classes to do what the type-system cannot
class ExistingFile:
def __init__(self, location: str):
if not os.path.exists(location):
raise Exception("File does not exist")
self.location = location
18. Generics
CommandData = TypeVar('CommandData')
class Command(Generic[CommandData]):
def __init__(self, data: CommandData, issuer: CommandIssuer):
self.data = data
self.issuer = issuer
List is a wrapper of something generic.
19. Putting it all together
Rethinking the paradigm of modelling software
We are biased towards OOP and RDBSs
25. Avoid flag types
Status = Literal["unvalidated", "validated"]
Book = NamedTuple('Book', [('author_data', Any), ('title', str), ('status', Status)])
def persist(book: Book):
if book.status != 'validated':
raise Exception("Cannot save unvalidated")
#save here
26. Every business subworkflow returns a new type
UnvalidatedBookData = NamedTuple('UnvalidatedBookData', [('author_data', Any), ('title', str)])
ValidatedBookData = NewType('ValidatedBookData', UnvalidatedBookData)
PersistedBookData = NewType('PersistedBookData', ValidatedBookData)
def persist(book: ValidatedBookData) -> PersistedBookData:
...
28. Workflow - dependencies
LoadOpenLibraryBookReference = Callable[['ExistingFile', Optional['LinesToLoadLimit']], BookReferences]
LoadOpenLibraryBookData = Callable[[OpenLibraryBookReference], 'UnvalidatedBookData']
PersistBook = Callable[['ValidatedBookData'], PersistedBookData]
class Workflow:
def __init__(self, book_reference_loader: LoadOpenLibraryBookReference, book_data_loader: LoadOpenLibraryBookData,
persist_book: PersistBook):
self.book_reference_loader = book_reference_loader
self.book_data_loader = book_data_loader
self.persist_book = persist_book
A OOP class constructor is a
higher order function that
injects types and returns an
aggregated type(the object)
with the state injected and
the set of callables it contains
32. Limitations of mypy - no recursive types
Employee = NamedTuple('Employee', [('id', Id), ('manager', 'Employee')])
mypy tree.py
tree.py:4: error: Cannot resolve name "Employee" (possible cyclic definition)
Found 1 error in 1 file (checked 1 source file)
33. Limitations of mypy
no private constructors without hack
no inheritance of union type or aliasing
34. Key learnings
When types are cheap you can model all valid states of your business with it.
Every function that change something in the business should return a new type.
Make illegal states impossible by modelling all valid ones in the type-system
Strive to represent the system state by the type-system
35. Complements and alternatives of mypy
pyre - facebook
pytype - google
pyannotate - automatically annotate your functions (Dropbox)
monkeytype: generate runtime annotations for types (instagram)
36. Resources
● Podcast: Gradual Typing of Production Applications
● Example of mypy in GYG: Search service
● Our journey to type checking
● DDD at 10 years
● Okreads source applying this ideas
37. The end
Thanks for listening
Just like the builders of Europe’s great Gothic cathedrals we’ve been honing our craft to the limits of material and structure. There is
an unfinished Gothic cathedral in Beauvais, France, that stands witness to this deeply human struggle with limitations. It was intended
to beat all previous records of height and lightness, but it suffered a series of collapses. Ad hoc measures like iron rods and wooden
supports keep it from disintegrating, but obviously a lot of things went wrong. From a modern perspective, it’s a miracle that so many
Gothic structures had been successfully completed without the help of modern material science, computer modelling, finite element
analysis, and general math and physics. I hope future generations will be as admiring of the programming skills we’ve been displaying
in building complex operating systems, web servers, and the internet infrastructure. And, frankly, they should, because we’ve done all
this based on very flimsy theoretical foundations. We have to fix those foundations if we want to move forward. - Bartosz Milewski’s