4 ms·
I think one of the most important features of typeclasses is the ability to be polymorphic on just the return type of an expression. For example, there is a typ
by tikhonj 14y ago
I think one of the most important features of typeclasses is the ability to be polymorphic on just the return type of an expression. For example, there is a typeclass called Read which comes with a function called read:
read :: Read r => String -> r
That is, you get a function from a String to whatever type is in the typeclass. It's the opposite of toString. This is also used in a whole bunch of other contexts like numbers--numeric literals are polymorphic, letting you add any numeric type you want and still use literals for it.
As far as I know, Go cannot do anything of the sort.
There are some other nice features of typeclasses, like the ability to have multiple types. That is, you could have a typeclass for multiplication that allowed you to multiply two numbers, two matrices or a number with a matrix. You can even have typeclasses that are recursive, allowing you to define them for an infinite amount of types: you could have a typeclass for functions of any number of arguments, for example.
I think Go interfaces do none of that as well.
Now Rust, on the other hand, actually* has typeclasses.
- SilasX 14y agoI'm personally on a quest to "get" Haskell right now, but I really don't see what's so special about that or unique to Haskell. Languages have long accomplished what `read` does by simply casting/coercing the string into the desired type. In python, for example, I'd call int() on the input strings I want to turn into integers.
- tikhonj 14y agoThe point is that you use a single function for any type. So in Python you'd have to do int() or float() or customType()... In Haskell, all of these would be just `read'. The type system can figure out which instance to use for you, without having to specify it. Moreover, it's also trivial to write a function that works on any readable type, something hard (although not entirely impossible) to do in Python. This makes it much easier to use: whenever you want to get any type from a string, you just read it. This is just like being able to print values of any type, except for parsing. This can also be used with constants rather than functions. So maxBound is the maximum value for any bounded type. In Python, the closest you can get to that is something like float.maxBound. (Except, apparently, it's actually sys.float_info.max.) As I mentioned, this also lets you define new numeric types that can still use the same literals. For my most recent project, I needed 18-bit words. I could do this and still write expressions like `x + 1` using the Word18 type. Moreover, it would be very easy to make my code generic over the exact type of number used--this would make it possible to use numbers of different sizes or even something more exotic like random variables. (It happens to be tricky because some of the semantics I was working with rely on having exactly 18 bits, but that's an issue with the domain and not with Haskell.) In another language, I would either have to use the normal int type and make sure to always keep track of the overflow myself or I would have to wrap every literal in a function that turned into an 18-bit word. So the special quality is being able to dispatch on the return type of an expression rather than on the types of the arguments. I think this is very special indeed and extremely useful. I hope this clarifies everything.
- SilasX 14y agoThat does make more sense, and I appreciate the more thorough explanation. It seems a bit ironic though, that Python makes you more specific and certain about the output type than Haskell!
- Peaker 14y agoIt is only ironic until you realize static typing can often save you from writing code, and not just require more code.
- SilasX 14y agoIn general, sure. Doesn't change that the "read" function seems to break the spirit of that.
- Peaker 14y agoHow so? Inferring which implementation of "read" to use based on the types is exactly the same spirit of inferring which implementation of "length" to use. Haskell can maintain consistency in both, but dynamically typed languages must revert to explicitly choosing the implementation in the former.
- SilasX 14y agoYes, it goes nicely with that spirit. But it doesn't go nicely with "have a well-specified return type for all functions that you know in advance", as "read" can return anything. It's great for languages to pick the right implementation of "length" on the fly, but the point is, they all return integers. "read"? Who knows what type you'll get back in Haskell, the "you must specify [or at least plan out the] output type" language? There, it seems to break the trend.
- Peaker 14y agoRead in Haskell returns a well defined type, it just happens to be polymorphic.
- billsix 14y agoOn your quest I'd recommend the following: http://www.amazon.com/gp/aw/d/0134843460 http://www.amazon.com/gp/aw/d/0134843460 http://www.cs.yale.edu/homes/hudak/SOE/ http://www.cs.yale.edu/homes/hudak/SOE/ http://book.realworldhaskell.org http://book.realworldhaskell.org
- SilasX 14y agoThanks, that second link looks really interesting!
- Peaker 14y agoAren't Rust "type-classes" also limited and use single-dispatch?
- tikhonj 14y agoI haven't used Rust very much yet, but my impression is that they can dispatch on the return type at the very least. So they can have a Num typeclass. Perhaps they're limited in some other ways, but it sounds like a good start.