(и не только)
10 декабря 2018 г.
@maksbotan
Система типов — это гибко управляемый синтаксический метод доказательства отсутствия в программе определенных видов поведения при помощи классификации выражений языка по разновидностям вычисляемых ими значений.
Бенджамин Пирс
«Типы в языках программирования»
David Hilbert
Alonzo Church
Alan Turing
Jean-Yves Girard
John C. Reynolds
Per Martin-Löf
J. Roger Hindley
Robin Milner
Thierry Coquand
Coq
Jeremy Siek
Philip Wadler
Типобезопасность
(корректность, type safety, soundness):
правильно типизированные выражения не ломаются
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. ... My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
Tony Hoare
PEP-3107: аннотация функций, 2006 г., Python 3.0
def add(a: int, b: int) -> int:
...
PEP-526: аннотация переменных, 2016 г., Python 3.6
a: int = 5
b: int = 10
print(add(a, b))
PEP-484: Type Hints, 2014 г.
from typing import List, Optional, TypeVar
T = TypeVar('T')
def head(xs: List[T]) -> Optional[T]:
...
PEP-483: The Theory of Type Hints, 2014 г.
a = 5
# У a тип int
b = 'abc'
# У b тип str
c = [42]
# У c тип List[int]
a = 5
a = 'abc' # Ошибка!
a = []
# ???
Generics — "функции на типах"
from typing import List
a: List[int] = [1,2,3]
b: int = a[0] # OK!
c: str = a[1] # NO!
d: str = 'abc'
a.append(d) # NO!
e: float = a[0] # OK?
f: float = 2.7
a.append(f) # HMMM...
g: Any = a[2] # OK
h: Any = 'BAM!'
a.append(h) # OK!
S
T
List[S]
List[T]
?
<:
FrozenSet[S]
?
FrozenSet[T]
Writer[S]
?
Writer[T]
S
T
List[S]
List[T]
<:
FrozenSet[S]
<:
FrozenSet[T]
Writer[S]
Writer[T]
:>
:(
S
T
<:
U
V
<:
Callable[[U], S]
?
Callable[[V], T]
Union[T1, T2, ...]
def foo(a: Union[bool, str]) -> None:
if isinstance(a, bool):
print(a is True)
else:
print(a == 'abc')
Tuple[T1, T2, ...]
def foo(a: Tuple[bool, str]) -> None:
(b, s) = a
print(b is True, s == "abc")
Sequence[T], Iterator[T], Iterable[T], Sized, Container[T]...
Ограниченные типовые переменные:
from typing import TypeVar
AnyStr = TypeVar('AnyStr', bytes, str)
def foo(a: AnyStr, b: AnyStr) -> AnyStr:
return a + b
foo = {'*': 42}
a = foo.get('?') # type: ???
Optional[Int]!
a: Optional[Int] = foo.get('?')
print(a * 5) # NO!
a: Optional[Int] = foo.get('?')
if a is not None:
print(a * 5) # type: int
else:
print('Sad!')
from my_library import get_angle
alpha: float = get_angle(x, y)
from someones_library import rotate
def rotate( coord: Tuple[float, float]
, alpha: float
) -> Tuple[float, float]: ...
print(rotate((x1, y1), alpha)
from typing import NewType
Degrees = NewType('Degrees', float)
Radians = NewType('Radians', float)
def degrees(a: Radians) -> Degrees: ...
def radians(a: Degrees) -> Radians: ...
angle: Degrees = get_angle()
def rotate(..., angle: Radians): ...
print(rotate((x1, y1), angle) # NO!
def length(vector: Tuple[float, float]) -> float:
return sqrt(vector[0]**2 + vector[1] ** 2)
my_age = 24
my_height = 183
print(length((my_age, my_height)) # WAT?
from typing import NamedTuple
class Vector(NamedTuple):
x: float
y: float
def length(vector: Vector) -> float:
...
Any namedtuple can be accidentally compared to any other with the same number of fields.
from dataclasses import dataclass
@dataclass
class Vector():
x: float
y: float
@dataclass
class Contact:
name: Name
email: EmailInfo
postal: PostalInfo
"Контакт должен содержать email или почтовый адрес"
@dataclass
class Contact:
name: Name
email: Optional[EmailInfo]
postal: Optional[PostalInfo]
noone = Contact('noone', None, None)
# OOOPS...
AddressInfo = Union[
EmailInfo,
PostalInfo,
Tuple[EmailInfo, PostalInfo]
]
@dataclass
class Contact():
name: Name
address: AddressInfo
def make_contact( name : Name
, email : Optional[EmailInfo]
, postal: Optional[PostalInfo]
) -> Optional[Contact]:
if email is None and postal is None:
return None
if email is not None:
return Contact(name, email)
elif postal is not None:
return Contact(name, postal)
else:
return Contact(name, (email, postal))
@dataclass
class SuperSecretCreds():
username: str
password: str
def destroy_all(creds: SuperSecretCreds):
if is_valid(creds):
super_secret_api.boom(creds)
else:
raise Http403
def destroy_sneaky(creds: SuperSecretCreds):
super_secret_api.boom(creds) # OOH...
ValidCreds = NewType('ValidCreds', SuperSecretCreds)
def boom(creds: ValidCreds) -> None:
...
def validate(creds: SuperSecretCreds)
-> Optional[ValidCreds]:
...
def destroy_all(creds: SuperSecretCreds):
validated: Optional[ValidCreds] = validate(creds)
if validated is not None:
super_secret_api.boom(validated)
else:
raise Http403
a: List[int] = [1, 2, 3]
print(a[42]) # BOOM!
def dot( a: List[float]
, b: List[float]
) -> float:
...
a: List[float] = [1,2,3]
b: List[float] = [5,6]
print(dot(a, b)) # OOOPS...
exactLength
: (len : Nat)
-> (xs : Vect m a)
-> Maybe (Vect len a)
Main> :t printf "%d %s"
printf "%d %s" : Int -> String -> String
x = tf.placeholder(
tf.float32, [None, 784]
)
y = tf.placeholder(
tf.float32, [None, 10]
)
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
type MNIST
= Network
'[ Convolution 1 10 5 5 1 1, Pooling 2 2 2 2, Relu
, Convolution 10 16 5 5 1 1, Pooling 2 2 2 2, Reshape, Relu
, FullyConnected 256 80, Logit, FullyConnected 80 10, Logit]
'[ 'D2 28 28, 'D3 24 24 10, 'D3 12 12 10, 'D3 12 12 10
, 'D3 8 8 16, 'D3 4 4 16, 'D1 256, 'D1 256
, 'D1 80, 'D1 80, 'D1 10, 'D1 10]
randomMnist :: MonadRandom m => m MNIST
randomMnist = randomNetwork
def sqrt(x: float) -> float:
...
print(sqrt(-1)) # HMMMM...
class NonEmpty(Generic[T], list):
def __init__( self
, h: T
, t: List[T]
) -> None:
self.extend([h] + t)
Modus Ponens:
Пусть А, B — утверждения
Значения — доказательства:
prfA: A = ...
prfB: B = ...prfA...
Какой тип у утверждения "из A следует B"?
def prfAiB(prfA: A) -> B:
return ...prfA...
AiB = Callable[[A], B]
prfA: A = ...
prfB: B = prfAiB(prfA)
AandB = Tuple[A, B]
def prf(prfAandBiC: Callable[[AandB], C])
-> Callable[[A], Callable[[B], C]]:
def prfAiBiC(prfA: A):
def prfBiC(prfB: B):
prfC: C = prfAandBiC(
(prfA, prfB)
)
return prfC
return prfBiC
return prfAiBiC
def prf( prfAiB: Callable[[A], B]
, prfBiC: Callable[[B], C]
) -> Callable[[A], C]:
def prfAiC(prfA: A) -> C:
prfB: B = prfAiB(A)
prfC: C = prfBiC(B)
return prfC
return prfAiC
Void = Callable[[], T]
Not = Callable[[T], Void]
def void() -> T:
return void()
def notA(prfA: T) -> Not[T]:
def prf(prfA: T) -> Void:
return void
return prf
Спасибо за внимание!