3 ms·
> This would allow a `plot` function in Python, originally written for the float data type, to also work for float-like (quacks like a duck) data types, like `D
by munificent 6y ago
> This would allow a `plot` function in Python, originally written for the float data type, to also work for float-like (quacks like a duck) data types, like `Decimal`.
So let's say you do that. You define your own new number-like type, say Complex. You pass that in to plot(n).
Somewhere in the body of plot(), though, it turns out there's some code like:
# Flip to put origin at bottom left.
window_height = 400
y = window_height - n
So now plot() tries to invoke "-" with an int on the left and your complex number on the right. It passes your complex number to the int class's minus operator, which has no idea about your type and barfs.
Python has a hacky workaround for this specific case where the right operand can implement __rsub__(), but that hacky workaround exists specifically because Python doesn't have multiple dispatch.
If it supported multiple dispatch natively, you'd just define an operator - that took a number on the left and a complex on the right, plot() would call it, and you'd be good.
- leephillips 6y agoWow, nice example. Made possible also because arithmetic operators in Julia are functions, and you can extend them to work on any data types. (Operators like "+" have hundreds of methods.)
- ChrisRackauckas 6y agoPlot recipes are a really good fundamental example of composability in the Julia ecosystem. Extending a plotting library cannot be a function because plotting functions don't compose. Want to support plotting quaternions in matplotlib? You'd write a `plotquaternion` function. Then the ODE solver makes a `plotode` function for the output object of its ODE solver. Now let's say you got ODEs solving on quaternions... what about plotting? Well you have to modify `plotode` to call `plotquaternion` or modify `plotquaternion` to call `plotode`, and this is how you get monorepos and the big functions we all know. Plot recipes are different. The Plots.jl system calls the type recipe function recursively on the plotting data `X` until it sees a set of primitives that it knows. Libraries define dispatches to this function to denote how data types should transform to something more primitives. Quaternion numbers become four independent series of numbers (and add keyword arguments for doing things like modulus). The ODE solver library writes a recipe that say, if you try and plot an ODE, transform it into arrays of time series. Now plot on a quaternion ODE solution recurses into 4x time series of the solution, which is the plot you'd want to have. This is a feature I use all of the time: I don't generally do more than just `plot(sol)` since recursive recipes generally give me the plot I want (or else I open an issue because someone is missing a recipe).
- musingsole 6y ago__rsub__ isn't a hacky workaround. Magic methods are a fundamental part of python's object model. If you think it's hacky, I think you don't grok python. > If it supported multiple dispatch natively, you'd just define an operator - that took a number on the left and a complex on the right, plot() would call it, and you'd be good. This doesn't come across as a win to me.
- StefanKarpinski 6y agoWhat happens when it's an operation that doesn't have magic fallback methods like the arithmetic operators do? Say the code is `max(400, n)` instead. Now what? The `max` function doesn't know about your special type, so the code fails. Magic methods simply don't scale: they solve this problem in a few very specific cases and nowhere else. With multiple dispatch, you have a completely general solution to this entire class of problems: you just define a new method of `max` that knows how to handle your new number type and everything works.