Real world example of Typeclasses > Interfaces

Very new to Haskell, but I was just mulling this over; when I first learned about typeclasses, I saw the apparent benefit of being able to assert that a parameter to a function belongs to two typeclasses instead of just one. Ie, in Java, for example, you can ensure that a parameter to a function belongs to a particular interface by doing something like: int doSomething(Comparable c) { ... } But there's no real way of ensuring an argument implements two interfaces, whereas in Haskell you can make sure that an argument belongs to two (or however many) typeclasses. doSomething :: (Ord a, Eq a, OtherTypeClass a) => a -> Int ... But as I thought about this, I couldn't seem to think of a practical case where this would be useful. The basic typeclasses in Haskell seem to follow a hierarchy. Ie, anything that belongs to Ord belongs to Eq, anything that belongs to Enum belongs to Ord, anything that belongs to Num belongs to Enum. I suppose there may be certain cases involving Functors where there would be a point to specifying multiple typeclasses (eg, an Int would not work with (Functor a, Ord a)), but I guess I'm still somewhat skeptical about how often such a check would be necessary in practical applications. So I guess I'm just wondering if someone could give me an example of a practical case of the typeclass system allowing you to do a check like this that would not have been possible with interfaces. Or is there another benefit to typeclasses over interfaces that I've missed?

On Fri, Sep 03, 2010 at 10:51:59PM -0400, Alec Benzer wrote:
But there's no real way of ensuring an argument implements two interfaces, whereas in Haskell you can make sure that an argument belongs to two (or however many) typeclasses.
doSomething :: (Ord a, Eq a, OtherTypeClass a) => a -> Int ...
But as I thought about this, I couldn't seem to think of a practical case where this would be useful.
This is quite useful, and it comes up all the time. I can't think of a particular example off the top of my head, but I know I have written lots of code with multiple type class constraints on the same type. However, this isn't actually an advantage over Java, since Java supports multiple interface inheritance. You can just declare a new interface that extends several other interfaces, and then use that new interface instead of having to list multiple ones. I don't think I'm qualified to comment on the precise differences between interfaces and type classes, since I haven't thought about it all that much. Perhaps others can chime in with some differences. There is this page on the Haskell wiki: http://www.haskell.org/haskellwiki/OOP_vs_type_classes -Brent

On Sat, Sep 4, 2010 at 12:29 PM, Brent Yorgey
On Fri, Sep 03, 2010 at 10:51:59PM -0400, Alec Benzer wrote:
But there's no real way of ensuring an argument implements two interfaces, whereas in Haskell you can make sure that an argument belongs to two (or however many) typeclasses.
doSomething :: (Ord a, Eq a, OtherTypeClass a) => a -> Int ...
But as I thought about this, I couldn't seem to think of a practical case where this would be useful.
This is quite useful, and it comes up all the time. I can't think of a particular example off the top of my head, but I know I have written lots of code with multiple type class constraints on the same type.
I know there are cases where this comes up in Haskell, but in certain cases the needs arises only because of the way Haskell handles typeclasses. What I mean is, if you had a function: something n = [1..n], and your type was a -> [a], a needs to be declared as (Num a, Enum a), even though this is sort of redundant, since you can't really have a number that isn't also enumerable. I'm talking specifically about cases where you'd have (TypeClass1 a, TypeClass2 a), and TypeClass1 doesn't imply TypeClass2 or visa versa (Brandon's example might have served that purpose, though I'm very new to Haskell and haven't gotten a full grasp on monads yet).
However, this isn't actually an advantage over Java, since Java supports multiple interface inheritance. You can just declare a new interface that extends several other interfaces, and then use that new interface instead of having to list multiple ones.
Ya but isn't that a pretty big hassle, especially assuming there are many useful cases where you require a type to belong to multiple typeclasses? In Java you'd have to declare a whole new interface for every single different set of interfaces you want your arguments to conform to, whereas in Haskell you can just specify them in a list. If cases come up enough where you do need to make sure your arguments conform to/belong to multiple interfaces/typeclasses, I'd see Haskell's system as a pretty major improvement, though this is what prompted me to ask the question in the first place, do such situations come up enough for this to be as big a benefit as it might seem to be.
I don't think I'm qualified to comment on the precise differences between interfaces and type classes, since I haven't thought about it all that much. Perhaps others can chime in with some differences. There is this page on the Haskell wiki:
Read through some of that (will probably read through more later), but as I did it also occurred to me that I guess the typeclass system might simply be a necessity to work with the rest of Haskell's type system? Ie, the point of them isn't their benefits over interfaces, it's that it's just the way things need to work in order to effectively connect with algebraic types?
-Brent _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

On 4 September 2010 18:04, Alec Benzer
What I mean is, if you had a function: something n = [1..n], and your type was a -> [a], a needs to be declared as (Num a, Enum a), even though this is sort of redundant, since you can't really have a number that isn't also enumerable.
Well, you can actually have a Num that isn't Enum. ghci> :info Num class (Eq a, Show a) => Num a where (+) :: a -> a -> a (*) :: a -> a -> a (-) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a -- Ozgur Akgun

I was speaking more generally, not specifically about the Haskell
typeclasses Num and Enum (although actually irrational numbers aren't
enumerable now that I think of it, but I also guess that's a
relatively moot point since you can't really represent irrational
numbers in a programming language)
On Sat, Sep 4, 2010 at 1:26 PM, Ozgur Akgun
On 4 September 2010 18:04, Alec Benzer
wrote: What I mean is, if you had a function: something n = [1..n], and your type was a -> [a], a needs to be declared as (Num a, Enum a), even though this is sort of redundant, since you can't really have a number that isn't also enumerable.
Well, you can actually have a Num that isn't Enum.
ghci> :info Num class (Eq a, Show a) => Num a where (+) :: a -> a -> a (*) :: a -> a -> a (-) :: a -> a -> a negate :: a -> a abs :: a -> a signum :: a -> a fromInteger :: Integer -> a
-- Ozgur Akgun

Alec Benzer
I was speaking more generally, not specifically about the Haskell typeclasses Num and Enum (although actually irrational numbers aren't enumerable now that I think of it, but I also guess that's a relatively moot point since you can't really represent irrational numbers in a programming language)
There is a Num instance, which doesn't make sense to be an Enum instance: instance Num a => Num (a -> a) where f + g = \x -> f x + g x -- ... This makes some formulas look much nicer and more convenient to write: constOne :: Floating a => a -> a constOne = sin^2 + cos^2 Unfortunately Eq and Show are superclasses of Num, so you need a few bogus instances: instance Eq (a -> a) where (==) = undefined instance Show (a -> a) where show = const "<function>" As an alternative you can do this with the reader functor, too, although it doesn't look nearly as nice for the above formula: constOne :: Floating a => a -> a constOne = (+) <$> (^2) . sin <*> (^2) . cos It looks great for other formulas, though, and is more general: splits :: [a] -> [([a], [a])] splits = zip <$> inits <*> tails Greets, Ertugrul -- nightmare = unsafePerformIO (getWrongWife >>= sex) http://ertes.de/

On Sat, Sep 04, 2010 at 01:04:43PM -0400, Alec Benzer wrote:
On Sat, Sep 4, 2010 at 12:29 PM, Brent Yorgey
wrote: I know there are cases where this comes up in Haskell, but in certain cases the needs arises only because of the way Haskell handles typeclasses. What I mean is, if you had a function: something n = [1..n], and your type was a -> [a], a needs to be declared as (Num a, Enum a), even though this is sort of redundant, since you can't really have a number that isn't also enumerable. I'm talking specifically about cases where you'd have (TypeClass1 a, TypeClass2 a), and TypeClass1 doesn't imply TypeClass2 or visa versa (Brandon's example might have served that purpose, though I'm very new to Haskell and haven't gotten a full grasp on monads yet).
No, I really do mean that this comes up all the time in a nontrivial way. Here's an example I just pulled randomly from a library of mine: -- | Create a scale transformation. scaling :: (HasLinearMap v, HasLinearMap (Scalar v), Scalar (Scalar v) ~ Scalar v, Fractional (Scalar v)) => Scalar v -> Transformation v scaling s = fromLinear $ (s *^) <-> (^/ s) None of those class constraints implies any of the others (not even in theory), and they are all necessary.
Ya but isn't that a pretty big hassle, especially assuming there are many useful cases where you require a type to belong to multiple typeclasses?
Sure, it's a hassle, but you were looking for examples of why type classes are better than interfaces, and I'm not sure that simple matters of convenience really count.
Read through some of that (will probably read through more later), but as I did it also occurred to me that I guess the typeclass system might simply be a necessity to work with the rest of Haskell's type system? Ie, the point of them isn't their benefits over interfaces, it's that it's just the way things need to work in order to effectively connect with algebraic types?
No, you don't need type classes to use algebraic data types; see OCaml. Type classes were a major innovation at the time they were added to Haskell, but algebraic data types had been around for a while. -Brent

On Sat, Sep 4, 2010 at 1:54 PM, Brent Yorgey
On Sat, Sep 04, 2010 at 01:04:43PM -0400, Alec Benzer wrote:
On Sat, Sep 4, 2010 at 12:29 PM, Brent Yorgey
wrote: I know there are cases where this comes up in Haskell, but in certain cases the needs arises only because of the way Haskell handles typeclasses. What I mean is, if you had a function: something n = [1..n], and your type was a -> [a], a needs to be declared as (Num a, Enum a), even though this is sort of redundant, since you can't really have a number that isn't also enumerable. I'm talking specifically about cases where you'd have (TypeClass1 a, TypeClass2 a), and TypeClass1 doesn't imply TypeClass2 or visa versa (Brandon's example might have served that purpose, though I'm very new to Haskell and haven't gotten a full grasp on monads yet).
No, I really do mean that this comes up all the time in a nontrivial way. Here's an example I just pulled randomly from a library of mine:
-- | Create a scale transformation. scaling :: (HasLinearMap v, HasLinearMap (Scalar v), Scalar (Scalar v) ~ Scalar v, Fractional (Scalar v)) => Scalar v -> Transformation v scaling s = fromLinear $ (s *^) <-> (^/ s)
None of those class constraints implies any of the others (not even in theory), and they are all necessary.
Ya but isn't that a pretty big hassle, especially assuming there are many useful cases where you require a type to belong to multiple typeclasses?
Sure, it's a hassle, but you were looking for examples of why type classes are better than interfaces, and I'm not sure that simple matters of convenience really count.
For what I meant I'd classify that sort of convenience as a benefit. I mostly was curious about Learn You a Haskell for Great Good's remark that "You can think of [typeclasses] kind of as Java interfaces, only better." Not having to create a new interface every time you want to check for a different collection of typeclasses would qualify as "better" for me.
Read through some of that (will probably read through more later), but as I did it also occurred to me that I guess the typeclass system might simply be a necessity to work with the rest of Haskell's type system? Ie, the point of them isn't their benefits over interfaces, it's that it's just the way things need to work in order to effectively connect with algebraic types?
No, you don't need type classes to use algebraic data types; see OCaml. Type classes were a major innovation at the time they were added to Haskell, but algebraic data types had been around for a while.
-Brent
participants (4)
-
Alec Benzer
-
Brent Yorgey
-
Ertugrul Soeylemez
-
Ozgur Akgun