How does `instance FromJSON a => FromJSON (Entity a)` work in Haskell? - 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.

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

Related

Single tag constructors in Aeson

I have a data type like this:
data A = A T.Text deriving (Generic, Show)
instance A.ToJSON A
If I use A.encode to it:
A.encode $ A "foobar" -- "foobar"
Then I use singleTagConstructors on it:
instance A.ToJSON A where
toEncoding a = A.genericToEncoding $ A.defaultOptions { A.tagSingleConstructors = True }
A.encode $ A "foobarquux" -- "{tag: A, contents: foobarquux}"
At some point I made another data type:
newtype Wrapper a = Wrapper
{ unWrap :: a
} deriving (Show)
instance A.ToJSON a => A.ToJSON (Wrapper a) where
toJSON w = A.object [ "wrapped" A..= unWrap w ]
Here's the part where I get confused:
A.encode $ Wrapper $ A "foobar" -- "{wrapped: foobar}"
How do I get the result to be like this?
"{wrapped: {tag: A, contents: foobarquux}}"
To answer the question directly, you can always implement the Wrapper instance with tagSingleConstructors = False, like this:
instance Generic a => A.ToJSON (Wrapper a) where
toJSON w = A.object [ "wrapped" A..= encA (unWrap w) ]
where
encA = A.genericToEncoding $ A.defaultOptions { A.tagSingleConstructors = False }
But I don't see why you'd want to do that.
If you control the API, then you don't need the tag field: the expected type of the wrapped value is already statically known, so tag would not be helpful.
And if you don't control the API, I would recommend representing it very explicitly, for example as a record that exactly matches the API shape. Otherwise you run a risk of accidentally breaking the API by making unrelated changes in remote parts of the codebase.
The issue is how you implemented your custom ToJSON instance.
instance A.ToJSON A where
toEncoding a = A.genericToEncoding $ A.defaultOptions { A.tagSingleConstructors = True }
Since you do not implement toJSON directly default implementation from typeclass definition is used.
class ToJSON a where -- excerpt from Data.Aeson.Types.ToJSON
-- | Convert a Haskell value to a JSON-friendly intermediate type.
toJSON :: a -> Value
default toJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
toJSON = genericToJSON defaultOptions
Effectively, you have the following instance:
instance A.ToJSON A where
toJSON = genericToJSON defaultOptions
toEncoding a = A.genericToEncoding $ A.defaultOptions { A.tagSingleConstructors = True }
While toEncoding uses expected tagged encoding, toJSON uses default encoding (which does not tag single constructors). This inconsistency is the root cause of the confusion. Later, in wrapper's ToJSON instance .= operator used. Internally, it uses toJSON and not toEncoding:
class KeyValue kv where
(.=) :: ToJSON v => Text -> v -> kv
infixr 8 .=
instance KeyValue Pair where
name .= value = (name, toJSON value)
As a solution, you should either define only toJSON and keep default toEncoding implementation (that uses toJSON) or implement both.

How do I derive PersistField for a custom newtype?

This is a Yesod-specific question, but even without knowing Yesod you might be able to help me, it has to do with newtypes.
Say I have the following simplified model in my config/models
Value
userId UserId
weight Weight
deriving (Show)
I will be using both kilograms and pounds in my webapp, but I decided that the DB should store things in kilograms. To get the type-system to protect me from confusing the two, I define the following:
newtype Weight = Kilograms Int
deriving (Read, Show, Eq, PersistField, PersistFieldSql)
That compiled fine, but how can I use this from a form?
logForm :: UserId -> Form Value
logForm uid = renderDivs $ Value <$>
pure uid <*>
areq intField "Weight" Nothing
I get the error
No instance for (Integral ModelTypes.Weight)
arising from a use of `intField'
I tried deriving Integral but then it complains I don't have Real Weight. On and on, I end up with:
newtype Weight = Grams Int
deriving (Read, Show, Eq, Enum, Ord, Num, Integral, Real, PersistField, PersistFieldSql)
Is this the correct way to do it? It seems like a lot of repetition. What's a better way to do it?
In general, if I have in Haskell a
newtype N = T a
for a concrete type a, how can I have N re-derive everything that a is in instance of, and also get N to derive some other typeclasses (in my example PersistField and PersistFieldSql). Thanks a lot.
A PersistField isn't the same as a Field. You want to make a custom field by importing Yesod.Forms. Here's an example, for a MathJax type ;)
newtype MathJax = MathJax { unMathJax :: Markdown }
deriving (Eq, Ord, Show, Read, PersistField, PersistFieldSql, IsString, Monoid)
unMM :: MathJax -> Text
unMM = unMarkdown . unMathJax
mathJaxField :: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m MathJax
mathJaxField = Field
{ fieldParse = parseHelper $ Right . MathJax . Markdown . Text.filter (/= '\r')
, fieldView = \theId name attrs val _isReq -> toWidget
[hamlet|$newline never
<textarea id="#{theId}" name="#{name}" *{attrs}>#{either id unMM val}
|]
, fieldEnctype = UrlEncoded
}

Proxies, type level symbols, and JSON

I'm trying to add automagical json parsing to Data.Vinyl
Here is an instance for FromJSON for records with exactly one element.
It almost works, but I can't satisfy the KnownSymbol constraint, it seems to auto generate a new type variable on me.
instance (KnownSymbol sym, FromJSON a) => FromJSON (PlainRec '[ sym ::: a ]) where
parseJSON (Object v) = (field =:) <$> (v .: json_name)
where field = Field :: (sym ::: a)
json_name = T.pack $ show field
The error is
Could not deduce (KnownSymbol sym0) arising from a use of ‛show’
from the context (KnownSymbol sym, FromJSON a)
More context http://lpaste.net/101005
If I replace all instances of sym with "name", it works, and runs and it is wonderful. Now, I could use template Haskell to generate all the instances ahead of time, since I have a closed list of field names that I'll actually use, but that seems like such a shame. I know next to nothing about Data.Proxy, having just seen in used to define the show instance for the records of Data.Proxy.
You just have to enable ScopedTypeVariables.

Convert Lens' a b into Lens' a (Maybe b)

I have several data structures like
data Data1 = Data1
{ _data1Field :: Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data1
data Data2 = Data2
{ _data2Field :: Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data2
-- More similar data types
So I decided to write a simple type class to make it easier to compose
class HasField a where
field :: Lens' a Int
instance HasField Data1 where
field = data1Field
instance HasField Data2 where
field = data2Field
But then I ran into the problem that some of these structures have the corresponding field as optional
data Data3 = Data3
{ _data3Field :: Maybe Int
-- More fields
} deriving (Eq, Show)
makeLenses ''Data3
And now I can no longer use the type class. Since there are about the same number of data types that have that field optional as not, I decided that it'd be better to change the typeclass:
class HasField a where
field :: Lens' a (Maybe Int)
instance HasField Data3 where
field = data3Field
But since I'm not very experienced with the lens library, I'm stuck figuring out how to make this new lens work with the types for Data1 and Data2. Ideally, I'd like to be able to view it and get a Maybe Int value for any type, and when setting I'd like Just x to set the field to x for Data1 and Data2 and be a no-op for those two types when passed Nothing.
Is this possible using existing combinators or am I going to have to write the lens myself? I'm fine doing so, but the majority of existing tutorials use TH and gloss over the details of writing one by hand.
I'm using GHC 7.6.3 and lens 3.10.
As a follow up to shachaf
class HasFieldA d where
field :: Traversal' d Int
instance HasFieldA Data1 where
field = data1Field -- Lens's are Traversals
instance HasFieldA Data3 where
field = data3Field . _Just
And then the ^? operator or the ^.. operator
getField :: HasFieldA d => d -> Maybe Int
getField = d ^? field -- or preview field d
to get it.
To set optional fields, you'd need another function
class SetFieldA d where
setField :: Setter' d Int
instance SetFieldA Data3 where
setField = set data3Field . Just

How do I write a ToJSON/FromJSON instance for a datatype with multiple constructors?

Every example I've seen for ToJSON and FromJSON are for data types with single constructors, like so :
data RewindConfig = RConfig JobID Phase
deriving Show
instance FromJSON RewindConfig where
parseJSON (Object o) = RConfig
<$> o .: "JobID"
<*> o .: "Phase"
parseJSON _ = fail "invalid RewindConfig"
I thought I would look to how Aeson makes the instance for a type with multiple constructors, for example Either:
instance (FromJSON a, FromJSON b) => FromJSON (Either a b) where
parseJSON (Object (H.toList -> [(key, value)]))
| key == left = Left <$> parseJSON value
| key == right = Right <$> parseJSON value
parseJSON _ = fail ""
The pattern-matching in parseJSON confuses me, I don't understand what is going on with (H.toList -> [(key, value)]).
The data type I want to make instances for looks like this:
data Foo = Bar String
| Baz String
| Bin String
it did occur to me to do something I knew how to implement
data Foo = (Maybe Bar) (Maybe Baz) (Maybe Bin)
But that seems unsatisfying. Could someone help me out by explaining what's going on with the Either instance, and perhaps giving me some guidance on To/From instances for Foo?
update: I think the instances Aeson implements for Maybe are much clearer and tells me what I need to know for my needs. Still, I'd like to know what's going on with Either.
The pattern (Object (H.toList -> [(key, value)])) is called a view pattern. You can read it as something like this:
parseJSon (Object o) = case H.toList o of
[(key, value)]
| key == left -> Left <$> parseJSON value
| key == right -> Right <$> parseJSON value
It's actually slightly different, since the above will always commit to the pattern Object o when handed an Object, whereas the view pattern will only commit when both the "matches the Object o pattern" and the "H.toList o matches the [(key, value)] pattern" conditions hold, but for this example that doesn't matter.
The json package contains an encoding for data types that you might want to adopt.
If you just derive Data you can use it. It's not very fast, but very easy to use.
Assuming each datatype has a distinct key, another approach could use lenses - I like it because it's concise and is readable. For example if you have a wrapper around an A, B, and C which all have FromJSON instances:
import Data.Aeson
import Data.Maybe
import Data.Aeson.Lens
import Control.Lens
data Wrap = WrapA A | WrapB B | WrapC C
instance FromJSON Wrap where
parseJSON json
| isJust (json ^? key "A's unique key") = WrapA <$> parseJSON json
| isJust (json ^? key "B's unique key") = WrapB <$> parseJSON json
| isJust (json ^? key "C's unique key") = WrapC <$> parseJSON json
| otherwise = fail "Bad message"

Resources