-1

I read the chapter about that topic in "learn you a haskell" and tried to find some hints on different websites - but are still unable to solve the following task. Im a haskell newbie (6 weeks of "experience") and its the first time I have to work with instances. So here is the task, my code has to pass the HUnit tests and the end. I tried to implement the instances but it seems like I´ve missed something there. Hope you can help me! THX

module SemiGroup where

{-

A type class 'SemiGroup' is given. It has exactly one method: a binary operation
called '(<>)'. Also a data type 'Tree' a newtype 'Sum' and a newtype 'Max' are
given. Make them instances of the 'SemiGroup' class.
The 'Tree' instance should build a 'Branch' of the given left and right side.
The 'Sum' instance should take the sum of its given left and right side. You need
a 'Num' constraint for that.
The 'Max' instance should take the maximum of its given left and right side. You
also need a constraint for that but you have to figure out yourself which one.
This module is not going to compile until you add the missing instances.

-}

import Test.HUnit (runTestTT,Test(TestLabel,TestList),(~?=))


-- | A semigroup has a binary operation.
class SemiGroup a where
    (<>) :: a -> a -> a

-- Leaf = Blatt, Branch = Ast
-- | A binary tree data type.
data Tree a = Leaf a
            | Branch (Tree a) (Tree a)
                deriving (Eq,Show)


-- | A newtype for taking the sum.
newtype Sum a = Sum {unSum :: a}


-- | A newtype for taking the maximum.
newtype Max a = Max {unMax :: a}

instance SemiGroup Tree where
    (<>) x y = ((x) (y))

instance SemiGroup (Num Sum) where
    (<>) x y = x+y

instance SemiGroup (Eq Max) where
    (<>) x y = if x>y then x else y



-- | Tests the implementation of the 'SemiGroup' instances.
main :: IO ()
main = do
    testresults <- runTestTT tests
    print testresults

-- | List of tests for the 'SemiGroup' instances.
tests :: Test
tests = TestLabel "SemiGroupTests" (TestList [
    Leaf "Hello" <> Leaf "Friend" ~?= Branch (Leaf "Hello") (Leaf "Friend"),
    unSum (Sum 4 <> Sum 8) ~?= 12,
    unMax (Max 8 <> Max 4) ~?= 8])

I tried something like:

class SemiGroup a where
    (<>) :: a -> a -> a

-- Leaf = Blatt, Branch = Ast
-- | A binary tree data type.
data Tree a = Leaf a
            | Branch (Tree a) (Tree a)
                deriving (Eq,Show)


-- | A newtype for taking the sum.
newtype Sum a = Sum {unSum :: a}


-- | A newtype for taking the maximum.
newtype Max a = Max {unMax :: a}

instance SemiGroup Tree where
    x <> y = Branch x y

instance Num a => SemiGroup (Sum a) where
    x <> y = x+y

instance Eq a => SemiGroup (Max a) where
    x <> y = if x>y then x else y

But there a still some failures left! At least the wrap/unwrap thing that "chi" mentioned. But I have no idea. maybe another hint ? :/

  • 1
    For simplicity's sake, I think you should choose only one tyhpeclass for the question instead of 3. Keeping things simple makes it easier to get good answers. Your question title is also very vague. – hugomg Jul 03 '14 at 19:21
  • 2
    The Tree instance of SemiGroup doesn't look like it will work, perhaps you meant `(<>) x y = Branch x y` – genisage Jul 03 '14 at 19:35
  • `instance SemiGroup Tree where` should be `instance SemiGroup (Tree a) where` and `instance Eq a => SemiGroup (Max a)` should be `instance Ord a => SemiGroup (Max a)`. You should read the errors that haskell gives you. These are just the type errors; your expressions are also incorrect so you will have to fix those. Hint: use pattern matching. Also, `unMax (Max 8 <> Max 4) ~?= 8` : the maximum of 4 and 4 is not 8... – user2407038 Jul 03 '14 at 21:09
  • You really need to read and get your head round Data.Monoid. That will show you how to do this task. Look it up on hoogle. – AndrewC Jul 03 '14 at 23:36

2 Answers2

1

I fail to see how to turn Tree a into a semigroup (unless it has to be considered up-to something).

For the Sum a newtype, you need to require that a is of class Num. Then, you need to wrap/unwrap the Sum constructor around values so that: 1) you take two Sum a, 2) you convert them into two a, which is a proper type over which + is defined, 3) you sum them, 4) you turn the result back into a Sum a.

You can try to code the above yourself starting from

instance Num a => Semigroup (Sum a) where
   x <> y = ...   -- Here both x and y have type (Sum a)

The Max a instance will require a similar wrap/unwrap code.


A further hint: to unwrap a Sum a into an a you can use the function

unSum :: Sum a -> a

to wrap an a into a Sum a you can use instead

Sum :: a -> Sum a

Note that both functions Sum, unSum are already implicitly defined by your newtype declaration, so you do not have to define them (you already did).

Alternatively, you can use pattern matching to unwrap your values. Instead of defining

x <> y = ... -- x,y have type Sum a (they are wrapped)

you can write

Sum x <> Sum y = ...   -- x,y have type a (they are unwrapped)
chi
  • 111,837
  • 3
  • 133
  • 218
  • Maybe I`m a bit untalented in haskell but to my mind this task is way to hard for a beginner who has to use instances for the first time :/ How can i convert the Sum a type to type a and backwards? Don`t find any information about it. – user3684652 Jul 03 '14 at 20:44
  • 1
    @chi: the desired implementation of `Semigroup` for `Tree a` is described in the comments. I see no problems there. – John L Jul 04 '14 at 04:10
  • @JohnL The proposed implementation is `(<>)=Branch` which is not associative. That is, unless you consider trees up-to the choice of the root. – chi Jul 04 '14 at 06:33
  • @chi: that's a fairly standard interpretation of trees; it's quite common to consider just the order of leaves and ignore the rest of the tree structure. I do admit that the test doesn't reflect this, but I don't think it's an issue for this purpose. – John L Jul 04 '14 at 09:04
0

Pay attention to the types. Either manually, or with some help from GHCi, figure out the type of the functions you are writing -- you'll find they don't match the types that the typeclass instance needs. You'll use wrapping and unwrapping to adjust the types until they work.