Lance Pollard
Lance Pollard

Reputation: 79278

How does `instance FromJSON a => FromJSON (Entity a)` work in Haskell?

I am new to Haskell. I am trying to create a simple JSON API client, and have found one implemented for Twitter in Haskell. My current goal is outlined in this question, but the same thing is demonstrated below.

In that Twitter/Haskell API code, there is this snippet:

https://github.com/himura/twitter-types/blob/master/Web/Twitter/Types.hs#L577-L587

type EntityIndices = [Int]

data Entity a = Entity {
  entityBody :: a,                -- ^ The detail information of the specific entity types (HashTag, URL, User)
  entityIndices :: EntityIndices, -- ^ The character positions the Entity was extracted from
} deriving (Show, Eq)

instance FromJSON a => FromJSON (Entity a) where
  parseJSON v@(Object o) = Entity <$> parseJSON v
                                  <*> o .: "indices"
  parseJSON _ = mzero

What is happening here?

First, from my understanding, That data block is a Generalized Algebraic Data Type, because you are passing a parameter into the type data Entity a, and that a is being used in entityBody :: a.

Second, how do you instantiate that Entity generalized algebraic data type?

Finally, what is happening here?

instance FromJSON a => FromJSON (Entity a) where

What does that => mean?

I can break this into multiple questions if that helps, but it all seems sorta interconnected.

Upvotes: 2

Views: 499

Answers (1)

DiegoNolan
DiegoNolan

Reputation: 3766

The definition for Entity

data Entity a = Entity {
  entityBody :: a,                -- ^ The detail information of the specific entity types (HashTag, URL, User)
  entityIndices :: EntityIndices, -- ^ The character positions the Entity was extracted from
} deriving (Show, Eq)

a can be of any type. There are no restrictions. The instance for FromJSON for Entity puts a restraint on the type of a. It is saying this instance of FromJSON for Entity the a type must also have an instance of FromJSON defined.

For example the show typeclass.

show :: Show a => a -> String

In order to call the show function the argument passed in a must have an instance of Show. The => just separates the typeclass constraints for the type definition.

So back to the FromJSON. If you define your own data type.

data Person = Person { name :: String } 

And write the code

let e = eitherDecode data :: Either String (Entity Person)

It won't compile because you haven't defined an instance of FromJSON for Person. If you create the instance then it will work.

instance FromJSON Person where
    parseJSON (Object o) = Person <$> o .: "name"
    parseJSON _ = mzero 

Upvotes: 3

Related Questions