5 ms·
This looks really cool. I get multiple dispatch, multi methods, and CLOS method specialization confused in my head. I can understand what this does... But when
by paddy_m 1y ago
This looks really cool. I get multiple dispatch, multi methods, and CLOS method specialization confused in my head. I can understand what this does... But when do you use it.
Can people list some times when they actually used multimethods to solve a real problem? How did it work out? Would you do it again?
- nbadg 1y agoAlso, dependent types, IE value-based dispatch. Which can be incredibly useful when dealing with enums. That alone is enough to make me curious to try it!
- paddy_m 1y agoValue based dispatch is a much better name for it then Dependent Types. I have seen the term dependent types and just glossed over it because I thought it was a more complex topic.
- breuleux 1y agoYeah, I suppose. I wrote "dependent" because I think that's the term of art of it, but you're making me think I should probably change the header to something more intuitive to people unfamiliar with type theory.
- paddy_m 1y agoI was just commenting that I had glossed over that in multiple readings about different typing systems. The parent of my original comment explained it nicely. I know that dependent type is the term of art, and you should probably keep it. You could say something along the lines of "ovld supports dependent types (an additionally specific name for a type that is based on its value, ie > 0)" the first time you use the term dependent types."
- SatvikBeri 1y agoMy company has used Julia for about 5 years now. 90% of the time in application code you only need single dispatch, same as OOP. One case where I actually rely on multiple dispatch is conversion to more or less structured data. Inspired by Clojure, I have a function `conform(OutputDataType, input_data::SomeType)` and specific implementations depend on both types involved. Multiple dispatch is also really helpful when two libraries don't quite do the same thing. In python pandas, numpy, and pytorch all have slightly different implementations of x.std() (standard deviation) with slightly different APIs. This means you can't write code that's generic across a numpy array or a pytorch tensor. In Python this could only easily be fixed if library authors coordinate, but with multiple dispatch you can just fix this in your own code.
- paddy_m 1y agoThat makes sense for writing your own generic libraries. Do you frequently have to convert from pytorch to numpy? I would think that a for a given execution unit (pipeline step, single program, model running on a server) that the data will stay as the same type after some initial conversion.
- SatvikBeri 1y agoI'd say we have about 60% library code and 40% application code internally, so making it easier to write libraries is really nice. E.g. you don't want to have to write different statistical calculations for every type you use. In particular, the fact that these types would silently give you different answers if you called `.std()` was a big headache It was very common for us to want to be generic over pandas series and numpy arrays. A bit less so with pytorch tensors, but that was because we just aggressively converted them to numpy arrays. Fundamentally these three are all very similar types so it's frustrating to have to treat them differently.
- paddy_m 1y agowhich implementation of `std()` did you go with? I was writing unit tests once against histograms. That code is super finnicky, and I couldn't get pandas and polars numbers to tie out. I wasn't super concerned with the exact output for my application, just that number of buckets was the same and they were roughly the same size. Just bumping to a new version of numpy would result in test breakages because of floating point rounding errors. I'm sure there are much more robust numerical testing things I could have done
- perlgeek 1y agoA use case is when a sub or method needs to do different things based on the data type of the argument. Example: turning a data structure into a JSON string, solved with multi dispatch: https://github.com/moritz/json/blob/master/lib/JSON/Tiny.pm#L26-L53 https://github.com/moritz/json/blob/master/lib/JSON/Tiny.pm#... Another common useful example is constructors. A Date class might have constructors that accept Date(Int, Int where 1..12, Int where 1..31) # Y, M, D Date(Str where /^\d4-\d2-\d2$/) # YYYY-MM-DD Date(DateTime) # extract the date of a DateTime object etc. to make it very intuitive to use.
- breuleux 1y agoI use it quite a lot (having made it), most often when I need to recursively process complex heterogeneous data structures. Merging two data structures, treemap, processing a Python AST, etc. Also in any situation where you want to be able to register new behavior. One case where I'm finding it extremely useful is that I'm currently using ovld to implement a serialization library (https://github.com/breuleux/serieux https://github.com/breuleux/serieux, but I haven't documented anything yet). The arguments for deserialization are (declared_type, value, context). With multimethods, I can define * deserialize(type[int], str, Any) to deserialize a string into an int * deserialize(type[str], Regexp[r"^\$"], Environment) to deserialize $XYZ as an environment variable lookup but only if the context has the Environment type * deserialize(type[Any], Path, WorkingDirectory) to deserialize from a file That's one situation where ovld's performance and codegen capabilities come in handy: the overhead is low enough to remain in the same performance ballpark as Pydantic v2.
- blarg1 1y agoMakes naming functions easier, eg multiply(mat3,vec3), multiply(mat3,mat3), etc If the language is dynamically typed, you can use it for polymorphism.