Multiple Dispatch
Single Dispatch
Single Dispatch
- Code reuse via inheritance
- Code works with many different types
- Late binding
- Goes by a few names
- Virtual functions
- Dynamic dispatch
- Probably others
- Python: everything is virtual
- Not free
- Exercise: implement virtual functions in pure C
Example
class Animal:
def speak(self):
raise NotImplementedError()
class Dog(Animal):
def speak(self):
print('Woof!')
class Cat(Animal):
def speak(self):
print('Meow!')
def make_some_noise(animal: Animal):
animal.speak()
make_some_noise(Dog())
make_some_noise(Cat())
Bad Example
class Dog:
def woof(self):
print('Woof!')
class Cat:
def meow(self):
print('Meow!')
def make_some_noise(animal):
if isinstance(animal, Dog):
animal.woof()
elif isinstance(animal, Cat):
animal.meow()
make_some_noise(Dog())
make_some_noise(Cat())
Bad Example
class Bird:
def tweet(self):
print('Tweet!')
# Now go back and add another isinstance check
Multiple Dispatch
Naive Implementation
- Store a mapping of declared argument types to function
- Lookup function based on argument types when called
Naive Implementation
>>> @naive_dispatch(int, int)
>>> def add(a, b):
... return a + b
...
>>> add(1, 2)
3
>>> class MyInt(int):
... ...
...
>>> add(MyInt(1), 2)
KeyError: Your implementation is naive
Generalization of Single Dispatch
Look at every argument
- Single dispatch is the first argument or implied
- Instead, choose the function to call based on RTT of every argument
Why even care?
- More natural for certain classes of operations
- Enables overriding behavior without inheritance
- Very clean recursion/delegation
- Parsimonious user-facing APIs
- Libraries become a lot more externally extensible
- Case study: Julia
Example: JavaScript Addition
A Trustworthy Person
Example
from multipledispatch import dispatch
@dispatch(int, int)
@dispatch(float, int)
@dispatch(int, float)
@dispatch(float, float)
def javascript_add(a, b):
return float(a) + float(b)
@dispatch(str, int)
@dispatch(int, str)
def javascript_add(a, b):
return str(a) + str(b)
@dispatch(str, float)
def javascript_add(a, b):
if b.is_integer():
return a + str(int(b))
return a + str(b)
@dispatch(float, str):
def javascript_add(a, b):
if a.is_integer():
return str(int(a)) + b
return str(a) + b
>>> javascript_add(1, 2)
3.0
>>> javascript_add(1.0, 2)
3.0
>>> javascript_add(1, 'a')
'1a'
>>> javascript_add('a', 1.0)
'a1'
Example
from multipledispatch import dispatch
@dispatch(int, int)
@dispatch(float, int)
@dispatch(int, float)
@dispatch(float, float)
def javascript_add(a, b):
return float(a) + float(b)
@dispatch(str, int)
@dispatch(int, str)
def javascript_add(a, b):
return str(a) + str(b)
@dispatch(str, float)
def javascript_add(a, b):
if b.is_integer():
return a + str(int(b))
return a + str(b)
@dispatch(float, str):
def javascript_add(a, b):
if a.is_integer():
return str(int(a)) + b
return str(a) + b
>>> javascript_add(1, 2)
3.0
>>> javascript_add(1.0, 2)
3.0
>>> javascript_add(1, 'a')
'1a'
>>> javascript_add('a', 1.0)
'a1'
Implementation
- Graph algorithm
- Nodes: function signatures
- Edges: a -> b iff a is more specific than b (supercedes)
- Topologically sort the graph
- Call each function until we succeed or run out of things to call
- Last year an intern implemented variadic dispatch
Pandas Backend
Heavy use of MD
How?
- Define rules for each ibis operation
- Rules define the procedure for evaluating an op
Cast
@execute_node.register(ops.Cast, type(None), dt.DataType)
def execute_cast_null_to_anything(op, data, type, **kwargs):
return None
@execute_node.register(ops.Cast, datetime.datetime, dt.String)
def execute_cast_datetime_or_timestamp_to_string(op, data, type, **kwargs):
"""Cast timestamps to strings"""
return str(data)
@execute_node.register(ops.Cast, datetime.datetime, dt.Int64)
def execute_cast_datetime_to_integer(op, data, type, **kwargs):
"""Cast datetimes to integers"""
return pd.Timestamp(data).value
@execute_node.register(ops.Cast, pd.Timestamp, dt.Int64)
def execute_cast_timestamp_to_integer(op, data, type, **kwargs):
"""Cast timestamps to integers"""
return data.value
Multiple Dispatch
By Phillip Cloud
Multiple Dispatch
- 1,275