Where the verb lives: left vs right of the data
⬅ Previous: 1 — Interpreted vs compiled · Next: 3 — Threads & concurrency ➡
Area of a shape, two ways:
| call | verb | |
|---|---|---|
| OOP (Python) | shape.area() |
right, after the dot |
| dispatch (Julia) | area(shape) |
left, in front |
import math
class Shape: ...
class Circle(Shape):
def __init__(self, r): self.r = r
def area(self): return math.pi * self.r**2
class Rectangle(Shape):
def __init__(self, L, w): self.L, self.w = L, w
def area(self): return self.L * self.w
Circle(2.0).area() # left of dot picks
arealives outside the types; method picked from the argument type.
▶ Script: cell “1. One argument: one function, one method per type”
Collision depends on both shapes:
collide(a::Circle, b::Circle) = "two circles"
collide(a::Circle, b::Rectangle) = "circle and rectangle"
collide(a::Rectangle, b::Rectangle) = "two rectangles"
collide(Circle(1.0), Rectangle(2.0, 3.0)) # "circle and rectangle"a.collide(b) sees only a; b needs isinstance.▶ Script: cell “2. Two arguments: dispatch on the PAIR of types”
Bet first: what happens? Fix in one line?
▶ Script: cells “The pair we did not define: what happens?” and “Add the missing pair in one line”
MethodError: no method matching collide(::Rectangle, ::Circle)
Missing method = run-time error. No coverage check.
perimeter(c::Circle) = 2π * c.r
perimeter(r::Rectangle) = 2 * (r.L + r.w)
perimeter.([Circle(2.0), Rectangle(3.0, 4.0)]) # [12.57, 14.0]isinstance ladder).| add a type | add an operation | |
|---|---|---|
| OOP | easy (new class) | edit every class |
| dispatch | easy (new struct) |
easy (new function) |
A function = a collection of methods; a call picks one.
Bet first: circles and a rectangle: element type?
▶ Script: cells “3. Dispatch and speed: what container did
shapesbuild?” and “Is its element type concrete?”
Vector{Shape}: Shape is abstract, so which area?
Bet first: which total is faster, by how much? Then @code_typed.
▶ Script: cells “Same data, two containers: predict which is faster, and by how much” and “Why? Find the call to
areain each”
Vector{Shape}: ~10× slower than Vector{Circle}.
concrete: area chosen once, at compile time, inlined.mixed: run-time lookup in the loop, like Python.Dispatch is fast when types are concrete.
Only difference:
globis a global,xan argument.
@code_warntype: spot the red Any@code_warntype = @code_typed, uncertain types in color.
Bet first: which has red Anys? How big is the time gap?
▶ Script: cells “4. Type instability — same sum, global vs argument” to “The argument version”
sum_global: glob::Any, s::Any, Body::Any · sum_arg: all concreteAny+ = run-time dispatch, like Vector{Shape}, like PythonNote
Work in functions, data as arguments.
Same reason for
@btime mysum($v)(measuring properly):$makesva known value.
| Python (OOP) | Julia (dispatch) | |
|---|---|---|
| Method | obj.f(), inside |
f(obj), outside |
| Chosen from | one object | all arguments |
| New op | edit every class | new function |
| 2 args | isinstance |
method per pair |
| Speed | lookup per call | compiled if types known |
Later: same
*picks the GPU method onCuArray(GPU module).
singledispatch, multipledispatch.⬅ Previous: 1 — Interpreted vs compiled · Next: 3 — Threads & concurrency ➡