DantheMan
DantheMan

Reputation: 7417

Using a class in a datatype constructor

Currently I have something like this:

data MyData = SomeData | MoreData | EvenMore
data DataLists = DataLists [MyData] [MyData]

Which allows me to obviously do something like this:

myDataList :: DataLists
myDataList = DataLists [SomeData, MoreData] [EvenMore, SomeData]

But I would like to instead do something like this:

-- Static Module File:

class MyClass b where
    evaluate :: b -> Int

data SomeData = SomeData
data MoreData = MoreData
data EvenMore = EvenMore

instance MyClass SomeData where
    evaluate _ = 2
instance MyClass MoreData where
    evaluate _ = 3
instance MyClass EvenMore where
    evaluate _ = 4

data DataList = DataList [MyClass] [MyClass] -- Obviously this is wrong

I would like that part of my code to remain static. Because later I would like to add in some types using that module to put in the Data List. I need this separate because the overall module will be used in 2 different projects and I don't want to have to keep going into edit it.

data MyVeryOwnData = MyVeryOwnData
--I should be able to make up as many datastructures as I want

instance MyClass MyVeryOwnData where
   evaluate _ = 99

myList :: DataList
myList = DataList [MyVeryOwnData, SomeData] [SomeData, EvenMore]

I realize that I could simply put MyVeryOwnData in the module file like this:

data MyData = SomeData | MyVeryOwnData ...

instance MyClass MyData where
    evaluate SomeData = 2
    evaluate MoreData = 3
    ... etc.

But I dont want to do that since as I said before I want to add an arbitrary amount of datatypes in.

Upvotes: 4

Views: 174

Answers (1)

Tarrasch
Tarrasch

Reputation: 10547

What you're looking for is called Heterogenous lists, and haskell supports them in mainly two ways, Existentials or GADTs. I show you a runnable code example that solved the problem with existentials.

{-# LANGUAGE ExistentialQuantification #-}

class Eval b where
    evaluate :: b -> Int

data SomeData = SomeData
data MoreData = MoreData
data EvenMore = EvenMore

instance Eval SomeData where
    evaluate _ = 2
instance Eval MoreData where
    evaluate _ = 3
instance Eval EvenMore where
    evaluate _ = 4

data Evalable = forall a . Eval a => Evalable a

instance Eval Evalable where
    evaluate (Evalable a) = evaluate a

pack :: Eval a => a -> Evalable
pack = Evalable

data DataLists = DataLists [Evalable] [Evalable]

getFirst :: DataLists -> [Evalable]
getFirst (DataLists xs ys) = xs

main = do
  let dl = DataLists [pack SomeData, pack EvenMore] [pack MoreData]
  print . map evaluate . getFirst $ dl

It should be clear that you can add instance Eval MyVeryOwnData in outside modules, and you can then pack those values so they become Evalable and you can then put them inside your Datalists.

Upvotes: 6

Related Questions