4 ms·
As far as I'm concerned, the discussion I'm referring to started with this comment from realharo: "Usually if you need a bunch of differently-typed objects to f
by nrinaudo 11y ago
As far as I'm concerned, the discussion I'm referring to started with this comment from realharo: "Usually if you need a bunch of differently-typed objects to fulfill the same contract, you can just make them implement an interface".
The way I understood that is, if you have a f(a: A), and need to pass it a B and C, neither of which are subtypes of A, you can just create a new type D, have A, B and C extend D, and modify f to expect a D. My point was that it's not always practical nor even possible to do so.
A type class would be a good way to abstract over the type itself and simply require a specific behaviour, but by the time you have f(a: A) where A is a concrete type, and you can't modify f (because it comes from an external library, say), it's too late to retrofit type classes into it.
As for your other point - type classes being awkward when you have multiple possible instances for a given type - I agree, but must say that I'm spoiled: my primary use of type classes is with Scala, where they're just, when you come down to it, parameters to functions. You can let the compiler infer them when there is no ambiguity, but when you need to pass an int to a function that expects "A with a monoid", you always have the option of being explicit about which monoid instance to use. I believe Haskell treats them as implicit parameters as well but without the option to pass them explicitly.
Finally, I'm afraid you've lost me with your last bit - reifying the instance we care about and passing the dictionary as a record. I wish I had something smart to answer, but I'm not sure I actually understand what it means. I know enough Haskell to be able to be able to read it (a necessary skill if you have even the slightest interest in FP), but not much more than that. Maybe if I knew more I'd have a clearer understanding of what you mean?
- dllthomas 11y agoFleshing out my "other point": "Passing the dictionary as a record" is a pretty common alternative to typeclasses. The way it works is if you have a typeclass: class Foo a where foo :: ... bar :: ... you instead make a record that looks like: data Foo = Foo { foo :: ..., bar :: ... } This works great for a lot of things, and is often preferable to typeclasses when all you're doing is defining an interface. One objection is that things look slightly less tidy, but the main problem with it (where it's a problem at all) is that there's no way of identifying individual implementors of an interface by type anymore. An example where this relevant is Set. We could define the Set operations to take a comparison function, but we couldn't be sure that all comparison functions agree across all interactions with a particular set. What I mean by "reifying the instance" is indexing typeclasses with a (single constructor) type that identifies the instance, and then passing that around to let us select which instance we care about, even for the same underlying type. For example: {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} import Data.Function (on) class ParamOrd o a | o -> a where pcompare :: o -> a -> a -> Ordering data StringsOverLength = StringsOverLength data StringsLexicographically = StringsLexicographically data IntegersInOrder = IntegersInOrder data IntegersReversed = IntegersReversed data ReversedOrdering r = ReversedOrdering r instance ParamOrd IntegersInOrder Integer where pcompare _ = compare instance ParamOrd (ReversedOrdering r) Integer where pcompare o = flip (pcompare o) instance ParamOrd StringsOverLength String where pcompare _ = compare `on` length instance ParamOrd StringsLexicographically String where pcompare _ = compare Now we could write a new version of Set, `ParamSet o a` such that we can still enforce agreement while choosing our orderings with an argument rather than a newtype.
- dllthomas 11y agoTo be clear, I'm not meaning to criticize, just understand my confusion and explore terminology. I agree with your starting point, but to my mind type classes are precisely a way to "make [types] implement an interface". I completely agree that it is often impractical to do so by defining new types. I'll address the rest when I have a little more bandwidth - I hope you'll forgive me; between interesting projects at work, a new baby, and holiday goings on, attention is in high demand.
- nrinaudo 11y agoI didn't realise hn threads could go so deep. I understand that we're not arguing, and find the conversation interesting: I approach it from the point of view of someone who was mostly taught OO (and learned whatever CS theory he knows by himself), where you seem to have a more solid FP and theoretical background and entirely different viewpoint. As a new father myself, I absolutely understand that free time is scarce. Enjoy your baby and holidays!