
Hello all, I find myself trying to define a class, where a newtype would suffice. E.g. once I was pondering over "named" things and my initial thought was "well that's a class, which asks for a function 'getName'". But then here http://www.haskell.org/pipermail/haskell-cafe/2010-June/078803.html someone showed me a solution without any classes. Recently I was pondering over "timed things" (things which change at specific ponts in time) and again my initial thought was "that's a class, which asks for a method 'at'". Then I remembered things like State, i.e. data types which wrap around functions. State is not a class, which asks for a function 'nextState', it is all defined with data. Can someone give me some guidance, when to use classes and when to use data? Are the two concepts interchangeable in some scenarios? Martin

Well, classes are datatypes. The only difference is that the compiler is able to derive the value of the class for you. Small example. Let's say we have a class
class AppendNumber a where appendNumer :: a -> Integer -> a
some instances
instance AppendNumber Integer where appendNumber n d = n + d instance AppendNumber String where appendNumber s d = s ++ show d
and some functions with it
increment :: AppendNumber a => a -> a increment n = appendNumber n 1
that you can use with specific types
main = print $ increment 2048
Now, you can just as easy implement all that without classes:
data AppendNumber a = AppendNumber {appendNumber :: a -> Integer -> a} appendNumberInteger :: AppendNumber Integer appendNumberInteger = AppendNumber {appendNumber = \n d -> n + d} appendNumberString :: AppendNumber String appendNumberString = AppendNumber {appendNumber = \s d -> s ++ show d} increment :: AppendNumber a -> a -> a increment an n = appendNumber an n 1 main = print $ increment appendNumberInteger 2048
The only two differences are that you have to 1) give names to the instances, and 2) manually insert the correct values in two last lines ('an' and 'appendNumberInteger' respectively). Classes simplify things, telling the compiler "I only care about one value of that type, and here it is; insert it whenever suitable". Now, I haven't seen your code, but it seems that you attempted to use something like
class Named a where getName :: a -> String
and then you were shown the better option, which is to just use String. Well, by the rules above, it translates to
data Named a = Named {getName :: a -> String}
and in all your functions the value of type 'a' would be paired with, essentially, the function "a -> String". If that's the situation you find yourself in, it's only natural to unite both in one value like this:
data WithName = forall a. WithName a (a -> String)
and use one value instead of two. But that calls for more refactoring; namely, the "WithName" data type above is isomorphic to "String", since you can't do anything else with it rather than apply it's second component to the first one. That's the reasoning behind removing classes in a nutshell.
16.06.2014, 22:58, "martin"
Hello all,
I find myself trying to define a class, where a newtype would suffice.
E.g. once I was pondering over "named" things and my initial thought was "well that's a class, which asks for a function 'getName'". But then here http://www.haskell.org/pipermail/haskell-cafe/2010-June/078803.html someone showed me a solution without any classes.
Recently I was pondering over "timed things" (things which change at specific ponts in time) and again my initial thought was "that's a class, which asks for a method 'at'".
Then I remembered things like State, i.e. data types which wrap around functions. State is not a class, which asks for a function 'nextState', it is all defined with data.
Can someone give me some guidance, when to use classes and when to use data? Are the two concepts interchangeable in some scenarios?
Martin _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Am 06/16/2014 09:28 PM, schrieb Miguel Mitrofanov:
Now, you can just as easy implement all that without classes:
data AppendNumber a = AppendNumber {appendNumber :: a -> Integer -> a} appendNumberInteger :: AppendNumber Integer appendNumberInteger = AppendNumber {appendNumber = \n d -> n + d} appendNumberString :: AppendNumber String appendNumberString = AppendNumber {appendNumber = \s d -> s ++ show d} increment :: AppendNumber a -> a -> a increment an n = appendNumber an n 1 main = print $ increment appendNumberInteger 2048
I just stumbled across some other things: When I use classes I can implement one function in terms of another function of the same class. When I use data/newtype I can't seem to do this. So I can replace a class by a data/newtype only when it wraps around a single function. Also I can use as many parameters as I like in data/newtype, but not in classes (if I stick to standard haskell) Is this correct?

The bit about multiple parameters is, of course, correct. There is a MultiParamTypeClasses extension though. Also note that for some classes you'd need RankNTypes, like 'Monad' class:
class Monad m where return :: a -> m a ...
is translated as
data Monad m = Monad {return :: forall a. a -> m a, ...}
As for recursive definitions, well, that's the same issue I pointed before. Say you have a class
class Foo a where bar :: a -> Bool baz :: a -> Bool
and you implement it as
instance Foo Integer where bar n = n > 0 baz n = not (bar n)
Well, with datatypes you have
data Foo a = Foo {bar :: a -> Bool, baz :: a -> Bool}
and of course this would never work:
fooInteger :: Foo Integer fooInteger = Foo {bar = \n -> n > 0, baz = \n -> not (bar n)}
However this one will:
fooInteger = Foo {bar = \n -> n > 0, baz = \n -> not (bar fooInteger n)}
and it's the exact translation of the 'class' solution. Отправлено с iPad
17 июня 2014 г., в 1:03, martin
написал(а): Am 06/16/2014 09:28 PM, schrieb Miguel Mitrofanov:
Now, you can just as easy implement all that without classes:
data AppendNumber a = AppendNumber {appendNumber :: a -> Integer -> a} appendNumberInteger :: AppendNumber Integer appendNumberInteger = AppendNumber {appendNumber = \n d -> n + d} appendNumberString :: AppendNumber String appendNumberString = AppendNumber {appendNumber = \s d -> s ++ show d} increment :: AppendNumber a -> a -> a increment an n = appendNumber an n 1 main = print $ increment appendNumberInteger 2048
I just stumbled across some other things:
When I use classes I can implement one function in terms of another function of the same class. When I use data/newtype I can't seem to do this. So I can replace a class by a data/newtype only when it wraps around a single function.
Also I can use as many parameters as I like in data/newtype, but not in classes (if I stick to standard haskell)
Is this correct?
participants (4)
-
martin
-
MigMit
-
Miguel Mitrofanov
-
Tom Ellis