Single tag constructors in Aeson - haskell

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.

Related

Deriving Eq and Show for an ADT that contains fields that can't have Eq or Show

I'd like to be able to derive Eq and Show for an ADT that contains multiple fields. One of them is a function field. When doing Show, I'd like it to display something bogus, like e.g. "<function>"; when doing Eq, I'd like it to ignore that field. How can I best do this without hand-writing a full instance for Show and Eq?
I don't want to wrap the function field inside a newtype and write my own Eq and Show for that - it would be too bothersome to use like that.
One way you can get proper Eq and Show instances is to, instead of hard-coding that function field, make it a type parameter and provide a function that just “erases” that field. I.e., if you have
data Foo = Foo
{ fooI :: Int
, fooF :: Int -> Int }
you change it to
data Foo' f = Foo
{ _fooI :: Int
, _fooF :: f }
deriving (Eq, Show)
type Foo = Foo' (Int -> Int)
eraseFn :: Foo -> Foo' ()
eraseFn foo = foo{ fooF = () }
Then, Foo will still not be Eq- or Showable (which after all it shouldn't be), but to make a Foo value showable you can just wrap it in eraseFn.
Typically what I do in this circumstance is exactly what you say you don’t want to do, namely, wrap the function in a newtype and provide a Show for that:
data T1
{ f :: X -> Y
, xs :: [String]
, ys :: [Bool]
}
data T2
{ f :: OpaqueFunction X Y
, xs :: [String]
, ys :: [Bool]
}
deriving (Show)
newtype OpaqueFunction a b = OpaqueFunction (a -> b)
instance Show (OpaqueFunction a b) where
show = const "<function>"
If you don’t want to do that, you can instead make the function a type parameter, and substitute it out when Showing the type:
data T3' a
{ f :: a
, xs :: [String]
, ys :: [Bool]
}
deriving (Functor, Show)
newtype T3 = T3 (T3' (X -> Y))
data Opaque = Opaque
instance Show Opaque where
show = const "..."
instance Show T3 where
show (T3 t) = show (Opaque <$ t)
Or I’ll refactor my data type to derive Show only for the parts I want to be Showable by default, and override the other parts:
data T4 = T4
{ f :: X -> Y
, xys :: T4' -- Move the other fields into another type.
}
instance Show T4 where
show (T4 f xys) = "T4 <function> " <> show xys
data T4' = T4'
{ xs :: [String]
, ys :: [Bool]
}
deriving (Show) -- Derive ‘Show’ for the showable fields.
Or if my type is small, I’ll use a newtype instead of data, and derive Show via something like OpaqueFunction:
{-# LANGUAGE DerivingVia #-}
newtype T5 = T5 (X -> Y, [String], [Bool])
deriving (Show) via (OpaqueFunction X Y, [String], [Bool])
You can use the iso-deriving package to do this for data types using lenses if you care about keeping the field names / record accessors.
As for Eq (or Ord), it’s not a good idea to have an instance that equates values that can be observably distinguished in some way, since some code will treat them as identical and other code will not, and now you’re forced to care about stability: in some circumstance where I have a == b, should I pick a or b? This is why substitutability is a law for Eq: forall x y f. (x == y) ==> (f x == f y) if f is a “public” function that upholds the invariants of the type of x and y (although floating-point also violates this). A better choice is something like T4 above, having equality only for the parts of a type that can satisfy the laws, or explicitly using comparison modulo some function at use sites, e.g., comparing someField.
The module Text.Show.Functions in base provides a show instance for functions that displays <function>. To use it, just:
import Text.Show.Functions
It just defines an instance something like:
instance Show (a -> b) where
show _ = "<function>"
Similarly, you can define your own Eq instance:
import Text.Show.Functions
instance Eq (a -> b) where
-- all functions are equal...
-- ...though some are more equal than others
_ == _ = True
data Foo = Foo Int Double (Int -> Int) deriving (Show, Eq)
main = do
print $ Foo 1 2.0 (+1)
print $ Foo 1 2.0 (+1) == Foo 1 2.0 (+2) -- is True
This will be an orphan instance, so you'll get a warning with -Wall.
Obviously, these instances will apply to all functions. You can write instances for a more specialized function type (e.g., only for Int -> String, if that's the type of the function field in your data type), but there is no way to simultaneously (1) use the built-in Eq and Show deriving mechanisms to derive instances for your datatype, (2) not introduce a newtype wrapper for the function field (or some other type polymorphism as mentioned in the other answers), and (3) only have the function instances apply to the function field of your data type and not other function values of the same type.
If you really want to limit applicability of the custom function instances without a newtype wrapper, you'd probably need to build your own generics-based solution, which wouldn't make much sense unless you wanted to do this for a lot of data types. If you go this route, then the Generics.Deriving.Show and Generics.Deriving.Eq modules in generic-deriving provide templates for these instances which could be modified to treat functions specially, allowing you to derive per-datatype instances using some stub instances something like:
instance Show Foo where showsPrec = myGenericShowsPrec
instance Eq Foo where (==) = myGenericEquality
I proposed an idea for adding annotations to fields via fields, that allows operating on behaviour of individual fields.
data A = A
{ a :: Int
, b :: Int
, c :: Int -> Int via Ignore (Int->Int)
}
deriving
stock GHC.Generic
deriving (Eq, Show)
via Generically A -- assuming Eq (Generically A)
-- Show (Generically A)
But this is already possible with the "microsurgery" library, but you might have to write some boilerplate to get it going. Another solution is to write separate behaviour in "sums-of-products style"
data A = A Int Int (Int->Int)
deriving
stock GHC.Generic
deriving
anyclass SOP.Generic
deriving (Eq, Show)
via A <-𝈖-> '[ '[ Int, Int, Ignore (Int->Int) ] ]

How to "reuse" instance definitions from another typeclass while introduding minor differences?

I want to output my application's logs in JSON, but there are some ubiquitous data-types for which ToJSON instances are not defined - most notably SomeException and the entire Exception hierarchy of types.
I have two choices:
Define instances of ToJSON for such data-types in my application
Write my own type-class, say ToJsonLogs, and make it reuse ToJSON instances as much as possible.
The first is the path of "least resistance" but it has other implications. Since type-class instances are global in nature, I might end-up defining ToJSON instances that break something. Also, for the same data-structure, I might want the JSON in APIs to be different from the JSON in logs (for example, scrubbing keys, auth-tokens, and other sensitive data OR truncating very long text fields).
This questions is about exploring the second option. How do I go about doing something like the following:
class ToJsonLogs a where
toJsonLogs :: a -> Aeson.Value
default toJsonLogs :: (ToJSON a) => a -> Aeson.Value
toJsonLogs = toJSON
instance ToJsonLogs SomeException where
toJsonLogs = toJSON . displayException
I tried the above idea, but it failed at the very first step itself. Here's an example data-structure:
data SyncResult = SyncResult
{ resAborted :: !Bool
, resSuccessful :: !Int
, resFailed :: ![(Int, SomeException)]
} deriving (Show)
I can't derive ToJsonLogs without first deriving ToJSON for the entire data-structure. Derivation of ToJSON fails because of SomeException. Hence the title of this question.
I even tried fooling around with Generics, but as usual, got stuck again.
You are very close to a possible extension-free solution. The thing you should consider is to create a wrapper for the original ToJson class members:
class ToJsonLogs a where
toJsonLogs :: a -> Aeson.Value
newtype WrapToJson a = WrapToJson a -- actually an Identity
instance ToJson a => ToJsonLogs (WrapToJson a) where
toJsonLogs (WrapToJson x) = toJson x
-- example
logInt :: Int -> Aeson.value
logInt x = toJsonLogs (WrapJson x)
If you want to restrict the wrapper only for ToJson instances, you will need to enable few extensions:
{-# LANGUAGE GADTSyntax, ExistentialQuantifiaction #-}
data WrapToJson a where WrapToJson :: ToJson a => a -> WrapToJson a
If you don't enjoy this wrapper, you may hide it under another definition of toJsonLogs:
toJsonLogs' :: ToJson a => a -> Aeson.value
toJsonLogs' = toJsonLogs . WrapToJson

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.
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

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.

Ignoring/Overriding an Instance generated using TemplateHaskell

I'm using Aeson for some client-server stuff that I'm doing, encoding ADTs as Json. I'm using Data.Aeson.TH to generate the toJSON instances I need, but the instances generated for Map types are really ugly and awful to deal with.
I've defined my own, simpler encoding which just treats them as lists:
instance (ToJSON a, ToJSON b) => ToJSON (Map a b) where
toJSON m = toJSON $ toList m
Naturally, when I use this in my code, I get a Duplicate instance declarations error.
Is there a way to resolve this? I need to either tell Template Haskell NOT to generate the ToJson instance for Map, or I need to tell GHC to ignore that instance and use the one I supply. Can either of these be done?
Note that this isn't an "overlapping-instances" problem. I want to completely throw out the one instance, not mix it with the other one.
To tell GHC to ignore library-provided instance and use your own instead, you can wrap Map in a newtype:
newtype PrettyMap key val = PrettyMap (Map key val)
instance (ToJSON a, ToJSON b) => ToJSON (PrettyMap a b) where
toJSON (PrettyMap m) = toJSON $ toList m
Another solution is to really use OverlappingInstances:
data MyData = ...
$(deriveToJSON ... ''MyData)
instance ToJSON (Map Text MyData) where
toJSON = toJSON . toList

Resources