2
votes

I have the following data type:

 data User = User { name :: T.Text, bookCount :: Int ,memberSince :: DateTime ,
  email :: T.Text, password :: B.ByteString } 
  | UnverifiedUser { nameUnverified :: T.Text,   emailUnverified :: T.Text,
     secret :: Integer  }

And the following instance:

instance FromJSON User where
    parseJSON (Object o) | Just _ <- M.lookup "secret" o = UnverifiedUser <$> o .: "name" <*> o .: "email" <*> o .: "secret"
                         | otherwise = User <$>  o .: "name" <*>  o .: "bookCount" <*> o .: "memberSince" <*> o .: "email" <*> o .: "password"
    parseJSON _          = mzero

But User was split into two, because sometimes the document in couchdb does not have some fields like secret, or password. How could I make an fromJSON instance for the data type:

   data User = User { name :: T.Text, bookCount :: Int ,memberSince :: DateTime ,
      email :: T.Text, password :: B.ByteString , secret :: Integer  }

where some fields are mandatory and others are not ?

1
Did you know that different constructors for the same data type can share record field names (provided they also share record field types)? For your example, you could write data User = Verified { name :: T.Text, email :: T.Text, ... } | Unverified { name :: T.Text, email :: T.Text }.Daniel Wagner
@Daniel: While that is correct, it is often easier to have the common stuff in one data type and split out the different stuff into another: data User = User { name :: T.Text, email :: T.Text, verification :: Verification } with data Verification = Unverified { secret :: Integer } | Verified { memberSince :: DateTime, password :: T.Text }.hammar
@Daniel, no I did not. But i do not trust the unverified user enough to accept his name as verified.Hai
@Hai I'm not sure I understand the connection between trust and how the data type is defined.Daniel Wagner

1 Answers

4
votes

Why not use Maybe?

 data User =
   User {
     name            :: T.Text,
     bookCount       :: Int ,
     memberSince     :: DateTime ,
     email           :: T.Text,
     password        :: Maybe B.ByteString,
     nameUnverified  :: T.Text,
     emailUnverified :: T.Text,
     secret          :: Maybe Integer}

instance FromJSON User where
    parseJSON (Object o) = User <$>
         o .: "name"            <*>
         o .: "bookCount"       <*>
         o .: "memberSince"     <*>
         o .: "email"           <*>
         o .:? "password"       <*>
         o .: "nameUnverified"  <*>
         o .: "emailUnverified" <*>
         o .:? "secret"