# Lunch and Learn
## The Latest on Python's Type System
<img src="https://miro.medium.com/v2/resize:fit:4800/format:webp/1*XP9e7KuJAIYXPF7PN2tsUA.png" alt="pc" style="width:600px;"/>
---
## First of all.....why bother?
- Code clarity
- Self documenting
- Verification (linting / static type checkers)
- Refactoring ease
---
## Type annotations (Unions)
Streamlined syntax
```python
def mkdir(path: str, exist_ok=Optional[bool]) -> \
Optional[UnixError]
```
becomes
```python
def mkdir(path: str, exist_ok=bool|None) -> \
None | UnixError
```
---
## Type annotations (Lists)
Streamlined syntax
```python
def split(s:str) -> List[str]
```
becomes
```python
def split(s:str) -> [str]
```
---
## Type annotations (Tuples)
Streamlined syntax
```python
def boundaries(data:List[float]) -> Tuple[float, float]
```
becomes
```python
def boundaries(data: List[float]) -> (float, float)
```
---
## Type annotations (Numpy)
https://numpy.org/devdocs/reference/typing.tml
- `ArrayLike`: objects that can be converted to dtypes
- `DtypeLike`: objects that can be converted to dtypes
---
## Type annotations (Numpy Example)
```python
import numpy as np
import numpy.typing as npt
print(npt.NDArray)
> numpy.ndarray[typing.Any, numpy.dtype[+ScalarType]]
print(npt.NDArray[np.float64])
> numpy.ndarray[typing.Any, numpy.dtype[numpy.float64]]
NDArrayInt = npt.NDArray[np.int_]
a: NDArrayInt = np.arange(10)
```
---
### Literals
```python
## Creating from raw data
from typing import List, Literal, get_args
MaturityType = Literal['Monthly', 'Weekly',
'Quarterly', 'Daily']
MATURITY_TYPES : List[MaturityType] = list(get_args(MaturityType))
def parse_maturity_type(s: str) -> MaturityType:
"""Parses a maturity type safely from a string."""
# Type checkers will ordain your type from here!
if s in MATURITY_TYPES:
return s
else:
raise Exception(f"Not a maturity type: {s}.")
```
---
## Pattern matching
- Introduced in python 3.10
- No *sealed* classes, but there are work-arounds
---
## Enums
- Similar to Java and C# Enums
- Closed / Nominal
- Not a *Tagged Union* like Rust, OCaml, F#
```python
from enum import Enum
class Side(Enum):
BUY = 'B'
SELL = 'S'
def signed(self, price: float) -> float:
match self:
case Side.BUY:
return price
case Side.SELL:
return -price
```
---
## Adhoc error composition with Unions
```python!
class RejectReason:
...
class HttpRequestError:
...
class Order:
...
class Fill:
...
class Cancel:
...
def submit_order(order: Order) -> \
Fill | Cancel | RejectReason | HttpRequestError:
...
```
---
## Result Type (1)
```python
from typing import Generic, TypeVar
T=TypeVar('T')
E=TypeVar('E', bound=Exception)
class Result(Generic[T, E]):
def ok(self) -> T | None: return None
def error(self) -> E | None: return None
def unwrap(self) -> T:
if (ret := self.ok()) is not None:
return ret
elif (err := self.error()):
raise err
else: raise Exception("Nothing to unwrap")
```
---
## Result Type (2)
```python
from dataclasses import dataclass
@dataclass
class Ok(Reult[T,E]):
_ok : T
def ok(self): return self._ok
@dataclass
class Error(Result[T,E]):
_error : E
def error(self): return self._error
```
---
## Rustedpy Result (1)
https://github.com/rustedpy/result
> A result value can be either `Ok(value)` or `Err(error)`, with a way to differentiate between the two. Ok and Err are both classes encapsulating an arbitrary value. `Result[T, E]` is a generic type alias for `typing.Union[Ok[T], Err[E]]`.
---
## Rustedpy Result (2)
```python
from result import Ok, Err, Result
def get_user_by_email(email: str) -> Result[User, str]:
"""
Return the user instance or an error message.
"""
if not user_exists(email):
return Err('User does not exist')
if not user_active(email):
return Err('User is inactive')
user = get_user(email)
return Ok(user)
```
---
## Dry Python
https://github.com/dry-python/
**Monads for python!**
- `returns` library introduces `Maybe` and `Result` types
- with `bind`, `flow,` `map`, operators etc.
- "functional" dependency injection
---
## Type checking in practice
- Use `pyright` for linting and static type checking with vscode
- Alternatively can use `mypy`
- Consider the `typeguard` pytest plugin
- **Only as good as your type annotations!**
---
### Limitations
- No Tail Cail Optimization (TCO)
- `lambda` keyword is heavy handed
- No standardized result type or monad api
- Creator of python is anti-functional
---
### Thanks for joining!
Questions?
---
{"metaMigratedAt":"2023-06-18T04:33:53.004Z","metaMigratedFrom":"YAML","title":"The Latest on Python's \"Type\" System","breaks":true,"description":"View the slide with \"Slide Mode\".","contributors":"[{\"id\":\"bf4db049-911e-493a-ad5c-ae15eadcd73d\",\"add\":7357,\"del\":2405}]"}