Reputation: 379
Brand new to Haskell and trying to write a parser.
I've used attoparsec to successfully chop my input file up into tokens into an AST.
I now want to walk the AST and emit output from it. I thought I could do that by adding some generic routines to Token class by deriving from a type class and then providing specific functions where needed in an instance to emit code based on the Token type.
The code is probably easier to follow than my explanation. This is what I tried:
class AST a where
children :: a -> [a]
prefix :: a -> String
suffix :: a -> String
node :: a -> [String]
children v = []
prefix v = ""
suffix v = ""
node v = [prefix v] ++ (concatMap node $ children v) ++ [suffix v]
data Token = Line { lnName :: String, lnLines :: Int }
| LineList { llLines :: [Token] }
| Init String
| Main String
| Step { stId :: String, stDuration :: Float }
| Scene { scId :: String, scTokens :: [Token] }
| Sequence { sqId :: String , sqScenes :: [Token] }
| File {flContents :: [Token]} deriving (Show, AST)
So my understanding is that if I derive from the type class I've written that:
But I get an error from ghc that's not all that helpful
Parser.hs|27 col 60 error| Can't make a derived instance of
AST Token':
AST' is not a derivable class In the data declaration for `Token'
Fair enough, but why is that the case? Kind of at a loss as to how to fix it without any more information. Any help gratefully received.
I know this isn't a useful comment but I have to say, absolutely loving Haskell. It's been a joy to learn :)
Upvotes: 1
Views: 444
Reputation: 379
Thanks to Nicolas' explanation that deriving is only applicable to a specific set of typeclasses I've fixed my problem. My solution is slightly different from Nicolas's in that I can still retain generic functionality in AST rather than tie it to Token
class AST a where
children :: a -> [a]
prefix :: a -> String
suffix :: a -> String
node :: a -> [String]
children _ = []
prefix _ = ""
suffix _ = ""
node v = [prefix v] ++ (concatMap node $ children v) ++ [suffix v]
data Token = Line { lnName :: String, lnLines :: Int }
| LineList { llLines :: [Token] }
| Init String
| Main String
| Step { stId :: String, stDuration :: Float }
| Scene { scId :: String, scTokens :: [Token] }
| Sequence { sqId :: String , sqScenes :: [Token] }
| File {flContents :: [Token]} deriving (Show )
instance AST token where
-- per token overides added here
-- defaults run if none supplied
Thanks everyone
Upvotes: 2
Reputation: 24769
deriving
can be use for a limited, fixed list of typeclass. The problem here is that you need to tell the compiler that the function you have defined are an instance of class AST
for the datatype Token
, like so:
class AST a where
children :: a -> [a]
prefix :: a -> String
suffix :: a -> String
node :: a -> [String]
instance AST Token where
children v = []
prefix v = ""
suffix v = ""
node v = [prefix v] ++ (concatMap node $ children v) ++ [suffix v]
data Token = Line { lnName :: String, lnLines :: Int }
| LineList { llLines :: [Token] }
| Init String
| Main String
| Step { stId :: String, stDuration :: Float }
| Scene { scId :: String, scTokens :: [Token] }
| Sequence { sqId :: String , sqScenes :: [Token] }
| File {flContents :: [Token]}
deriving (Show)
Upvotes: 5