Felix Paulusma
Felix Paulusma

Reputation: 11

Error RECURSIVE data type Persistent model with YESOD

TL;DR How to make (Key record) an instance of Eq/Show/Read in Yesod


I'm having a problem with module recursion. I've declared some of my own data types in a file which I want to be available to Models.hs, since I need to use it in the config/models file. But one of the types needs data types from the DB. A FooId (i.e. Key Foo) to be precise.

It looks something like this:

config/models

Foo
    name Text
    thingy (Bar Foo) Maybe
    UniqueName name
    deriving Eq Show Read Typeable

My own file contains these

data Bar record = Bar { a :: [Jab record] }
  deriving (Eq, Show, Read, Typeable)

data Jab record = Jab { b :: Key record
                      , c :: Int
                      }
  deriving (Eq, Show, Read, Typeable)

Now I need to import Model.hs to get Key to work, but I also need to import my file into Model.hs for it to work. I've also tried adding my own types to Models.hs, but I get these errors:

Model.hs:24:13:
    No instance for (Eq (Key record))
      arising from the first field of ‘Jab’ (type ‘Key record’)
    Possible fix:
      use a standalone 'deriving instance' declaration,
        so you can specify the instance context yourself
    When deriving the instance for (Eq (Jab record))

And adding a "deriving instance Eq a => Eq (Key a)" together with the {-# LANGUAGE StandaloneDeriving #-} doesn't work either.

If anyone knows how to remedy this, it'd help me immensely.

Upvotes: 0

Views: 82

Answers (1)

Felix Paulusma
Felix Paulusma

Reputation: 11

Turns out writing my own instances for (Jab record) fixed this issue.

This is how I fixed it, for reference:

  • Removing the Eq, Show and Read from the deriving clause of Jab
  • Making my own Eq, Show and Read instances for (Jab record)
  • After that compiled I had to consequently make a PersistField instance for (Bar record)

(The following is all put in a separate file to be imported to Models.hs)

module Custom.TypesAndInstances where

import ClassyPrelude.Yesod

import Prelude (read)
import qualified Data.Text as T
import qualified Text.Read.Lex as L
import Text.ParserCombinators.ReadPrec
import GHC.Read
import GHC.Show (appPrec)

-- instance for PersistField (Bar record)

instance (PersistEntity record, PersistField record) => PersistField (Bar record) where
    toPersistValue = PersistText . T.pack . show
    fromPersistValue (PersistText t) = Right $ read $ T.unpack t

-- instances for Jab Eq, Read and Show

instance PersistEntity record => Eq (Jab record) where
    Jab x a == Jab y b = x == y && a == b  

instance PersistEntity record => Show (Jab record) where
    show (Jab x a) = "Jab " ++ show x ++ " " ++ show a

instance PersistEntity record => Read (Jab record) where
    readPrec = parens
        (prec appPrec (do
            expectP (L.Ident "Jab")
            x <- step readPrec
            y <- step readPrec
            return (Jab x y))
        )
    readListPrec = readListPrecDefault
    readList = readListDefault

----------------------
data Bar record = Bar { a :: [Jab record]
                                    }
  deriving (Eq, Show, Read, Typeable)

data Jab record = Jab { b :: Key record  
                      , c :: Int
                      }
  deriving (Typeable)

Upvotes: 1

Related Questions