I have recieved a exercice to learn about data types in haskell and I can't figure out how to solve this one.
They got me a data like : data CatLista a = Nil | Unit a| Conc (CatLista a) (CatLista a) deriving Eq
and I need to make the data types become: Nil -> [] , Unit x -> [x] , Conc -> same operator as ( )
So if you run Conc (Unit 9)(Conc (Unit 5) (Unit 3)) == Conc (Conc (Unit 9) (Unit 5))(Conc (Unit 3) Nil)
should give true and Conc (Unit 9)(Unit 3) == Conc (Unit 3) (Unit 9)
should give false.
I already tried instancing the show class like this:
instance Show a => Show (CatLista a) where
show a = case a of
Nil -> []
Unit x -> "[" show x "]"
Conc Nil dos -> show dos
Conc uno Nil -> show uno
Conc uno dos -> "[" show uno "," show dos "]"
I'm quite new to haskell so I may not know some basics, because I can't understand why if I run it (beeing Conc uno dos -> show uno show dos
) with the same command as bellow Conc (Unit 9)(Conc (Unit 5) (Unit 3)) == Conc (Conc (Unit 9) (Unit 5))(Conc (Unit 3) Nil)
it still returns False even tough they both return the same [9][5][3] with my show instance.
EDIT
Thanks to you I made it, it know returns correctly the values like intended with this code:
toLista :: Eq a => CatLista a -> [a]
toLista Nil = []
toLista (Unit x) = [x]
toLista (Conc a b)
| a == Nil = toLista b
| b == Nil = toLista a
| otherwise = ( ) (toLista a) (toLista b)
instance (Show a,(Eq a)) => Show (CatLista a) where
show a= show (toLista a)
But still I dont know why if I try the same comparation it still returns False, even tough I get returned the same [9,5,3].
This is probably due to my lack of knowledge in Haskell, sorry about that.
CodePudding user response:
Two values are not equivalent if these produce the same String
when calling show
on these. The (==) :: Eq a => a -> a -> Bool
function is implemented by the Eq
typeclass. You thus need to implement the instance
of Eq
for your CatLista
yourself.
The default implementation for Eq
, if you use deriving Eq
is that two values are the same if they have the same data constructor, and the parameters are elementwise equivalent by calling (==)
on these.
You thus can implement the instance for Eq
yourself with:
data CatLista a = Nil | Unit a| Conc (CatLista a) (CatLista a) -- ← no deriving Eq
instance Eq a => Eq (CatLista a) where
ca == cb = toLista ca == toLista cb
CodePudding user response:
Because you include deriving Eq
in the data declaration, you're telling the compiler to generate a default equality method. The compiler cannot read your mind and know that you intend to represent lists via concatenation of sublists.
You know you intend Conc [1, 2] [3]
to represent the same list as Conc [1] [2, 3]
1; either should be regarded as representing [1, 2, 3]
. The compiler does not know this. The generated equality method sees that in one case we have [1, 2]
in the first field of the Conc
constructor, and in the other case we have [1]
. Those two things aren't equal, so the default equality method says that the whole structure is not equal either.
To avoid that, stop messing around with the Show
instance. Show
is for converting a CatList
to a String
of code that would build it. It has nothing to do with testing equality. There's no possible way you can change the outcome of ==
by defining a Show
instance.
You need to remove deriving Eq
from the data declaration. That will mean the compiler doesn't generate a default equality method, so you can write your own one that does something different (compares whether two CatList
s represent the same list rather than comparing whether they have the same structure).
1 I am abusing notation here to write the sublists using bracket syntax; this is not valid code, I'm just trying to make the difference in sub-structure more easily visible.