from fastcore.dispatch import typedispatch
@typedispatch
def proc_fc(x: int): return x + 1
@typedispatch
def proc_fc(x: float): return x * 2
Fastcore migration guide
fastcore
to fasttransform
Fastai users
If you’re using fastai, there’s nothing you need to do - these changes will be included in future fastai releases and your existing code will continue to work as before.
Fastcore dispatch users
Fastcore’s type dispatch system is being replaced with Plum, a more robust multiple dispatch library. This section covers how to update your code that uses @typedispatch
or TypeDispatch
.
Install plum:
pip install plum-dispatch
Simple decorator migration
For most usecases switching to plum is as easy as switching out fastcore
’s typedispatch
for plum
’s dispatch
.
Before:
After:
from plum import dispatch
@dispatch
def proc_pl(x: int): return x + 1
@dispatch
def proc_pl(x: float): return x * 2
Converting TypeDispatch to Plum Function
If you’re using TypeDispatch
directly, you’ll need to use Plum’s Function
class instead.
The key differences are:
- Initialization uses
Function()
instead ofTypeDispatch()
- Methods are added using
.register()
or.dispatch()
- Function inspection uses
.methods
instead ofprint()
Initialization
# setup
import numbers
def f2(x:int, y:float)->float: return x+y #int and float for 2nd arg
def f_nin(x:numbers.Integral)->int: return x+1 #integral numeric
def f_ni2(x:int): return x #integer
def f_fl(x:float)->float: return x #float
def f_bll(x:bool|list)->bool|list: return x #bool or list
def f_num(x:numbers.Number)->numbers.Number: return x #Number (root of numerics)
= [
fs
f2,
f_nin,
f_ni2,
f_fl,
f_bll,
f_num ]
Before:
from fastcore.dispatch import TypeDispatch
= TypeDispatch(fs)
t_fc t_fc
(bool,object) -> f_bll
(int,float) -> f2
(int,object) -> f_ni2
(Integral,object) -> f_nin
(float,object) -> f_fl
(list,object) -> f_bll
(Number,object) -> f_num
After:
from plum import Function
= Function(f2)
t_pl for f in fs: t_pl.register(f)
len(t_pl.methods)
6
Adding new functions to an initialized TypeDispatch/Function
Before:
lambda x: x**2)
t_fc.add( t_fc
(bool,object) -> f_bll
(int,float) -> f2
(int,object) -> f_ni2
(Integral,object) -> f_nin
(float,object) -> f_fl
(list,object) -> f_bll
(Number,object) -> f_num
(object,object) -> <lambda>
After:
lambda x: x**2) # or t_pl.dispatch(lambda x: x**2)
t_pl.register( t_pl.methods
List of 7 method(s): [0] f2(x: int, y: float) -> float <function f2 at 0x10be6df80> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:4 [1] f2(x: numbers.Integral) -> int <function f_nin at 0x10be6dee0> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:5 [2] f2(x: int) <function f_ni2 at 0x10be6e020> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:6 [3] f2(x: float) -> float <function f_fl at 0x10be6e0c0> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:7 [4] f2(x: bool | list) -> bool | list <function f_bll at 0x10be6e160> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:8 [5] f2(x: numbers.Number) -> numbers.Number <function f_num at 0x10be6e200> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:9 [6] f2(x: Any) <function <lambda> at 0x10be85260> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/2634702974.py:1
Combining multiple instances of TypeDispatch/Function
Fastcore provided a way to initialize TypeDispatch with pre-existing TypeDispatch objects as its base(s).
Before:
def f_str(x:str): return x+'1'
= TypeDispatch(f_str, bases=t_fc)
t_fc2 t_fc2
(str,object) -> f_str
(bool,object) -> f_bll
(int,float) -> f2
(int,object) -> f_ni2
(Integral,object) -> f_nin
(float,object) -> f_fl
(list,object) -> f_bll
(Number,object) -> f_num
(object,object) -> <lambda>
Plum does not provide this feature directly. But we’ve written a function to help you accomplish a similar result.
After:
from fasttransform.transform import _merge_funcs
= Function(f_str).dispatch(f_str)
t_pl_new = _merge_funcs(t_pl_new, t_pl)
t_pl2 t_pl2.methods
List of 8 method(s): [0] f_str(x: int, y: float) -> float <function f2 at 0x10be6df80> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:4 [1] f_str(x: numbers.Integral) -> int <function f_nin at 0x10be6dee0> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:5 [2] f_str(x: int) <function f_ni2 at 0x10be6e020> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:6 [3] f_str(x: float) -> float <function f_fl at 0x10be6e0c0> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:7 [4] f_str(x: bool | list) -> bool | list <function f_bll at 0x10be6e160> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:8 [5] f_str(x: numbers.Number) -> numbers.Number <function f_num at 0x10be6e200> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/891647847.py:9 [6] f_str(x: Any) <function <lambda> at 0x10be85260> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/2634702974.py:1 [7] f_str(x: str) <function f_str at 0x10be85ee0> @ /var/folders/0h/5ktbjhg17ns6j8zgdsgvzvy80000gn/T/ipykernel_14825/3062264994.py:1
Obtain the raw function and return type for given arg types
If you wanted to retrieve the raw function that matches given runtime input types. Then with TypeDispatch you could use the __getitem__
method.
Before:
int] t_fc[
<function __main__.f_ni2(x: int)>
If you wanted to retrieve that raw function’s return type then you can use the .returns
method.
Before:
5) t_fc.returns(
Plum’s Function does not provided the exact same functionality. But you can pass arguments to .resolve_method
or ._resolve_method_with_cache
method to find the matching raw function and it’s return type.
After:
= t_pl.resolve_method((5,))
raw_func, ret_type print(raw_func)
print(ret_type)
<function f_ni2>
typing.Any
or the cached version:
= t_pl._resolve_method_with_cache((5,))
raw_func, ret_type print(raw_func)
print(ret_type)
<function f_ni2>
typing.Any
Breaking Change: Ambiguous Type Matching
A key behavioral difference in Plum is how it handles ambiguous type matches:
- Fastcore: Silently uses the last defined function when multiple matches exist
- Plum: Raises an
AmbiguousLookupError
to prevent unexpected behavior
Example:
@typedispatch
def f2_fc(x:int|float): return x+2
@typedispatch
def f2_fc(x:int|str): return x*3
5) f2_fc(
15
While plum will raise an AmbiguousLookupError
.
from plum import AmbiguousLookupError
@dispatch
def f2_pl(x:int|float): return x+2
@dispatch
def f2_pl(x:int|str): return x*3
try: f2_pl(5)
except AmbiguousLookupError: print("Caught expected AmbiguousLookupError")
Caught expected AmbiguousLookupError
Moving from fastcore to fasttransform
If you’re using Transform
or Pipeline
, follow these steps:
install fasttransform
Install the new package:
pip install fasttransform
Update imports
# Before
from fastcore.transform import Transform, Pipeline
# After
from fasttransform import Transform, Pipeline
Initializing Transform
In fasttransform, you can now define multiple encode/decode methods either through subclassing (like before) or directly:
# Before (subclassing required)
from fastcore.transform import Transform as FCTransform
class MyTransform(FCTransform):
def encodes(self, x:int): return "enc int!"
def encodes(self, x:str): return "enc str!"
def decodes(self, x:int): return "dec int!"
def decodes(self, x:str): return "dec str!"
= MyTransform()
fct fct
MyTransform:
encodes: (int,object) -> encodes
(str,object) -> encodes
decodes: (int,object) -> decodes
(str,object) -> decodes
While the old behavior is still supported, you can now also do the following:
# After (direct initialization possible)
from fasttransform import Transform
def my_transform(x:int): return "enc int!"
def my_transform2(x:str): return "enc str!"
def my_transform_dec(x:int): return "dec int!"
def my_transform_dec2(x:str): return "dec str!"
=(my_transform,my_transform2), dec=(my_transform_dec,my_transform_dec2)) Transform(enc
my_transform(enc:2,dec:2)
Advanced: Custom Transform Implementation
If you’ve overridden internal methods like _call
or _do_call
in your custom Transform classes:
- Review the new Plum-based implementation in the source code
- Update your methods to work with Plum’s
Function
class instead ofTypeDispatch
- If you need help, create an issue