Question

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:

  • I don't need to provide an instance definition as all of the functions have default implementations
  • I can override the defaults per Token type if needed

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 :)

Was it helpful?

Solution

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)

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top