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

Polymorphism and Typeclasses

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Polymorphism and Typeclasses

Avatar for Sobolev Nikita

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]"