Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Polymorphism and Typeclasses

Polymorphism and Typeclasses

Sobolev Nikita

November 14, 2021
Tweet

More Decks by Sobolev Nikita

Other Decks in Programming

Transcript

  1. inclusion / subtyping class Animal(object): def say(self) -> None: print('**

    wild animal noises **') class Dog(Animal): def say(self) -> None: print('woof') def say_all(animals: list[Animal]) -> None: for animal in animals: print(animal.say()) say_all([Animal(), Dog()])
  2. overloading from typing import overload @overload def sum_two(a: str, b:

    str) -> str: ... @overload def sum_two(a: int, b: int) -> int: ... def sum_two(a, b) -> str | int: return a + b
  3. @dataclass class MyUser(object): name: str def greet(instance: str | MyUser)

    -> str: if isinstance(instance, str): return 'Hello, "{0}"!'.format(instance) elif isinstance(instance, MyUser): return 'Hello again, {0}'.format(instance.name) raise NotImplementedError( 'Cannot greet "{0}" type'.format(type(instance)), )
  4. from typing_extensions import Protocol class CanGreet(Protocol): def greet(self) -> str:

    ... def greet(instance: CanGreet) -> str: return instance.greet()
  5. Минусы > Оно точно не сломает нашу объектную модель? >

    Объекты обрастают слишком большим числом методов
  6. Минусы > Оно точно не сломает нашу объектную модель? >

    Объекты обрастают слишком большим числом методов > Нет возможности расширить "чужой" код
  7. @dataclass class MyUser(object): name: str # Somewhere else: MyUser.greet =

    lambda self: 'Hi, {0}'.format(self.name) print(MyUser('a').greet())
  8. import abc from typing import Generic, TypeVar _Wrapped = TypeVar('_Wrapped')

    class BaseGreet(Generic[_Wrapped]): """Abstract class of all other """ def __init__(self, wrapped: _Wrapped) -> None: self._wrapped = wrapped @abc.abstractmethod def greet(self) -> str: raise NotImplementedError
  9. class StrGreet(BaseGreet[str]): def greet(self) -> str: return 'Hello, {0}!'.format(self._wrapped) @dataclass

    class MyUser(object): name: str class MyUserGreet(BaseGreet[MyUser]): def greet(self) -> str: return 'Hello again, {0}'.format(self._wrapped.name)
  10. @typeclass def greet(instance) -> str: raise NotImplementedError( 'Cannot greet "{0}"

    type'.format(type(instance)), ) @greet.instance(str) def _greet_str(instance: str) -> str: return 'Hello, "{0}"!'.format(instance) @greet.instance(MyUser) def _greet_myuser(instance: MyUser) -> str: return 'Hello again, {0}'.format(instance.name) greet('world') # => Hello, "world"! greet(MyUser('example')) # => Hello again, example
  11. @typeclass def greet(instance) -> str: raise NotImplementedError( 'Cannot greet "{0}"

    type'.format(type(instance)), ) @greet.instance(str) def _greet_str(instance: str) -> str: return 'Hello, "{0}"!'.format(instance) @greet.instance(MyUser) def _greet_myuser(instance: MyUser) -> str: return 'Hello again, {0}'.format(instance.name) greet('world') # => Hello, "world"! greet(MyUser('example')) # => Hello again, example
  12. @typeclass def greet(instance) -> str: raise NotImplementedError( 'Cannot greet "{0}"

    type'.format(type(instance)), ) @greet.instance(str) def _greet_str(instance: str) -> str: return 'Hello, "{0}"!'.format(instance) @greet.instance(MyUser) def _greet_myuser(instance: MyUser) -> str: return 'Hello again, {0}'.format(instance.name) greet('world') # => Hello, "world"! greet(MyUser('example')) # => Hello again, example
  13. @doc "Our custom protocol" defprotocol Greet do # This is

    an abstract function, # that will behave differently for each type. def greet(data) end @doc "Enhancing built-in type" defimpl Greet, for: BitString do def greet(string), do: "Hello, #{string}!" end @doc "Custom data type" defmodule MyUser do defstruct [:name] end @doc "Enhancing our own type" defimpl Greet, for: MyUser do def greet(user), do: "Hello again, #{user.name}" end
  14. @doc "Our custom protocol" defprotocol Greet do # This is

    an abstract function, # that will behave differently for each type. def greet(data) end @doc "Enhancing built-in type" defimpl Greet, for: BitString do def greet(string), do: "Hello, #{string}!" end @doc "Custom data type" defmodule MyUser do defstruct [:name] end @doc "Enhancing our own type" defimpl Greet, for: MyUser do def greet(user), do: "Hello again, #{user.name}" end
  15. @doc "Our custom protocol" defprotocol Greet do # This is

    an abstract function, # that will behave differently for each type. def greet(data) end @doc "Enhancing built-in type" defimpl Greet, for: BitString do def greet(string), do: "Hello, #{string}!" end @doc "Custom data type" defmodule MyUser do defstruct [:name] end @doc "Enhancing our own type" defimpl Greet, for: MyUser do def greet(user), do: "Hello again, #{user.name}" end
  16. @doc "Our custom protocol" defprotocol Greet do # This is

    an abstract function, # that will behave differently for each type. def greet(data) end @doc "Enhancing built-in type" defimpl Greet, for: BitString do def greet(string), do: "Hello, #{string}!" end @doc "Custom data type" defmodule MyUser do defstruct [:name] end @doc "Enhancing our own type" defimpl Greet, for: MyUser do def greet(user), do: "Hello again, #{user.name}" end
  17. trait Greet { fn greet(&self) -> String; } impl Greet

    for String { fn greet(&self) -> String { return format!("Hello, {}!", &self); } } struct MyUser { name: String, } impl Greet for MyUser { fn greet(&self) -> String { return format!("Hello again, {}", self.name); } }
  18. trait Greet { fn greet(&self) -> String; } impl Greet

    for String { fn greet(&self) -> String { return format!("Hello, {}!", &self); } } struct MyUser { name: String, } impl Greet for MyUser { fn greet(&self) -> String { return format!("Hello again, {}", self.name); } }
  19. trait Greet { fn greet(&self) -> String; } impl Greet

    for String { fn greet(&self) -> String { return format!("Hello, {}!", &self); } } struct MyUser { name: String, } impl Greet for MyUser { fn greet(&self) -> String { return format!("Hello again, {}", self.name); } }
  20. trait Greet { fn greet(&self) -> String; } impl Greet

    for String { fn greet(&self) -> String { return format!("Hello, {}!", &self); } } struct MyUser { name: String, } impl Greet for MyUser { fn greet(&self) -> String { return format!("Hello again, {}", self.name); } }
  21. fn greet(instance: &dyn Greet) -> String { return instance.greet(); }

    pub fn main() { println!("{}", greet(&"world".to_string())); // Hello, world! println!("{}", greet(&MyUser { name: "example".to_string(), })); // Hello again, example }
  22. -- Our custom typeclass class Greet instance where greet ::

    instance -> String -- Enhancing built-in type with it instance Greet String where greet str = "Hello, " ++ str ++ "!" -- Defining our own type data MyUser = MyUser { name :: String } -- Enhancing it instance Greet MyUser where greet user = "Hello again, " ++ (name user)
  23. -- Our custom typeclass class Greet instance where greet ::

    instance -> String -- Enhancing built-in type with it instance Greet String where greet str = "Hello, " ++ str ++ "!" -- Defining our own type data MyUser = MyUser { name :: String } -- Enhancing it instance Greet MyUser where greet user = "Hello again, " ++ (name user)
  24. -- Our custom typeclass class Greet instance where greet ::

    instance -> String -- Enhancing built-in type with it instance Greet String where greet str = "Hello, " ++ str ++ "!" -- Defining our own type data MyUser = MyUser { name :: String } -- Enhancing it instance Greet MyUser where greet user = "Hello again, " ++ (name user)
  25. -- Our custom typeclass class Greet instance where greet ::

    instance -> String -- Enhancing built-in type with it instance Greet String where greet str = "Hello, " ++ str ++ "!" -- Defining our own type data MyUser = MyUser { name :: String } -- Enhancing it instance Greet MyUser where greet user = "Hello again, " ++ (name user)
  26. greetAlias :: Greet instance => instance -> String greetAlias =

    greet main = do print $ greetAlias "world" -- Hello, world! print $ greetAlias MyUser { name="example" } -- Hello again, example
  27. implementation = self._exact_types.get(instance_type, None) if implementation is not None: return

    implementation for protocol, callback in self._protocols.items(): if isinstance(instance, protocol): return callback return _find_impl(instance_type, self._exact_types)
  28. from classes import AssociatedType, Supports, typeclass class Greet(AssociatedType): """Special type

    to represent that some instance can `greet`.""" @typeclass(Greet) def greet(instance) -> str: """No implementation needed.""" @greet.instance(str) def _greet_str(instance: str) -> str: return 'Hello, {0}!'.format(instance) def greet_and_print(instance: Supports[Greet]) -> None: print(greet(instance)) greet_and_print('world') # ok greet_and_print(1) # Argument 1 to "greet_and_print" has incompatible type "int"; # expected "Supports[Greet]"