3 ms·
This question is very OT considering the what TFA is about, but… What does the term ad-hoc polymorphism mean in your comment? Are you saying Rust does not have
by throwaway17_17 4y ago
This question is very OT considering the what TFA is about, but…
What does the term ad-hoc polymorphism mean in your comment? Are you saying Rust does not have ad-hoc polymorphism because the syntax acts like the function in parametrically polymorphic and only once monomorphisation occurs are the different functions per argument type are generated is it N different functions. Does this line of thinking also say Haskell does not have ad-hoc polymorphism?
And in case any reader wonders, I am genuinely interested in your response to these questions (i.e. this isn’t bait nor rhetorical).
- JoelMcCracken 4y agoIndeed; I am wondering why go does not consider traits in rust a form of ad hoc polymorphism?
- deleted 4y ago[deleted]
- tialaramex 4y agoAd hoc polymorphism is distinct from parametric polymorphism in that rather than parameter type itself being a parameter, there are just an arbitrary (ad hoc determined) set of types allowed. The C++ standard library defines contains(x) so that it'll work for a string x, a single char x or a pointer to chars x. Nothing else can work, those are the arbitrary list of types which work, that's ad hoc polymorphism. A separate implementation is provided for each of those three cases, which is why if I've got a JPEG, I can't instead ask if the string contains the JPEG, that's not one of the three implementations provided. The Rust standard library just defines the type of x in terms of a trait, Pattern. So, my Jpeg type can just implement Pattern and now I can ask if Strings contain the Jpeg and that works because contains delegates the matching problem to Pattern and Jpeg implements Pattern -- only the specific idea of "contains" as distinct from "begins with" or "split" or a dozen other functions is handled by the contains function. I am not a (serious) Haskell programmer but I would argue that Haskell also lacks ad hoc polymorphism here as I understand it (and to be clear: I think this is in general a good or at worst reasonable choice) The place where ad hoc really shines is when you're got a function that say, makes complete sense with exactly two (or maybe three, fewer than two is irrelevant and more than three seems unlikely to provide reasonable ergonomics) specific types, which otherwise have nothing useful in common. For example suppose I've got a whole variety of bird types, Goose, Chicken, Ostrich, Penguin, Sparrow and so on, and I've got this function thunk() and I realise that, oddly it makes sense to thunk a Sparrow or an Ostrich, but literally no other birds at all. I think hard about it, but the best I can come up with to describe this property is "Thunkable" since all it really means is you can thunk() them. And there's no "content", there's no special implementation work in "Thunkable" that shouldn't live in thunk() for maintenance anyway. In this case ad hoc polymorphism is great because it saves needing to make this stupid "Thunkable" trait / type class / type-of-types / interface just to group together Sparrow and Ostrich for this single purpose. But I'd argue the "Thunkable" case is rare, and C++ has a lot of cases where ad hoc polymorphism was the wrong choice and they fell into it. I mentioned three types, really C++ contains() does only two things but it spells one of them two ways for 20+ year old reasons. It can do strings (as std::string, but also via C's char * type) and it can do a single character char (one code unit, so, no poop emoji). Rust provides four Patterns, they are a string reference, a single char (a Unicode scalar, so yes a poop emoji works), a slice of chars (any of the chars matches), or a predicate which matches characters. Now, do any of those four feel like things you'd definitely never want in C++? Because if C++ wanted all of them that's now five overloads for contains. And five overloads for find, and for every other matching function, on every string or string-like type... I believe ad hoc polymorphism is so rarely what you really want, and yet it so often detracts from the rest of the language facilities that "We don't have that" is a sensible language design choice, same as for multiple inheritance.
- throwaway17_17 4y agoI think my issue is with your definition. Type classes in Haskell are the implementation for ad-hoc polymorphism, and likewise traits in Rust. I think the definition you are using is not the commonly given one and that is where my confusion came from. Typeclasses (in a Haskell context) we’re formalized in the Wadler and Blott paper “How to make ad-hoc polymorphism less ad hoc”. And for Rust, in [1] Traits are explicitly stated to be the method in which Rust achieves ad-hoc polymorphism. 1: Klabnik, Steve; Nichols, Carol (2019-08-12). "Chapter 10: Generic Types, Traits, and Lifetimes". The Rust Programming Language (Covers Rust 2018)
- tialaramex 4y agoI didn't know either of these things, I guess now I have some reading to do, so thanks. Edited to add: Hmm. Actually though, surely that second reference is just "The Book" as it's called, did it actually say it's about ad hoc polymorphism? Because I've read this section of The Book, although I hadn't in 2018, and it doesn't mention "Ad hoc polymorphism". What's there now (I just checked) is a description of how you'd approach this problem in Rust, using traits, but doesn't claim this is ad hoc polymorphism, and sure enough doesn't involve an arbitrary set of types which is the sort of the point of why "ad hoc" is there in the name.
- throwaway17_17 4y agoThe standard definition of ad-hoc polymorphism imputed to Strachy, where: Strachey chose the adjectives ad-hoc and parametric to distinguish two varieties of polymorphism [Str67]. Ad-hoc polymorphism occurs when a function is defined over several different types, acting in a dif- ferent way for each type. A typical example is overloaded multiplication: the same symbol may be used to denote multiplication of integers (as in 33) and multiplication of floating point values (as in 3.143.14). That is from the Wadler paper where typeclasses are formalized. Typeclasses and Traits are the implementation details for those function symbols that vary in implementation for each type. The restrictions on the types (like which types implement a trait or have a type class defined for it) are the types the symbol can be used on. You seem to be focusing on the ‘arbitrary set of types’ point, but the only connection between the types accepted by Rust’s generic functions (which are functions that accept a type provided it has some trait) are that they take types which have an impl for that trait. I think there is a bit of ambiguity regarding the term ad-hoc polymorphism at play. You seem to think the trait/typeclass implementation of ad-hoc polymorphism (which was invented to formalize a well behaved class of ad-hoc polymorphic functions) makes it no longer ad-hoc. My position echos Wadler, it’s still ad-hoc but just less ‘ad-hoc’ (i.e. more formalized).