Reputation: 9104
I'm having trouble declaring ToJSON
instances of my type (synonyms):
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module Argon.Types (ComplexityBlock, AnalysisResult, ResultsOptions(..)
, OutputMode(..))
where
import Data.Aeson
import qualified Data.ByteString.Lazy.Char8 as BL
-- | Hold the data associated to a function binding:
-- (line number, column, function name, complexity)
type ComplexityBlock = (Int, Int, String, Int)
instance ToJSON ComplexityBlock where
toJSON (l, c, func, cc) = object [ "lineno" .= l
, "col" .= c
, "name" .= func
, "complexity" .= cc
]
-- | Represent the result of the analysis of one file.
-- It can either be an error message or a list of
-- 'ComplexityBlock's.
type AnalysisResult = Either String [ComplexityBlock]
instance ToJSON (FilePath, AnalysisResult) where
toJSON (p, Left err) = object [ "path" .= p
, "type" .= "error"
, "message" .= err
]
toJSON (p, Right rs) = object [ "path" .= p
, "type" .= "result"
, "blocks" .= rs
]
Having imported the bytestring package and enabled the OverloadedStrings
extension I thought it could work, but it does not:
/home/miki/exp/argon/src/Argon/Types.hs:31:47:
No instance for (ToJSON a0) arising from a use of ‘.=’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance ToJSON (FilePath, AnalysisResult)
-- Defined at src/Argon/Types.hs:29:10
instance ToJSON ComplexityBlock
-- Defined at src/Argon/Types.hs:17:10
In the expression: type .= error
In the first argument of ‘object’, namely
‘[path .= p, type .= error, message .= err]’
In the expression:
object [path .= p, type .= error, message .= err]
/home/miki/exp/argon/src/Argon/Types.hs:31:50:
No instance for (Data.String.IsString a0)
arising from the literal ‘error’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Data.String.IsString Value
-- Defined in ‘aeson-0.8.0.2:Data.Aeson.Types.Internal’
instance (a ~ Data.ByteString.Internal.ByteString) =>
Data.String.IsString
(attoparsec-0.12.1.6:Data.Attoparsec.ByteString.Internal.Parser a)
-- Defined in ‘attoparsec-0.12.1.6:Data.Attoparsec.ByteString.Char8’
instance Data.String.IsString
Data.ByteString.Builder.Internal.Builder
-- Defined in ‘Data.ByteString.Builder’
...plus four others
In the second argument of ‘(.=)’, namely ‘error’
In the expression: type .= error
In the first argument of ‘object’, namely
‘[path .= p, type .= error, message .= err]’
/home/miki/exp/argon/src/Argon/Types.hs:35:46:
No instance for (ToJSON a1) arising from a use of ‘.=’
The type variable ‘a1’ is ambiguous
Note: there are several potential instances:
instance ToJSON (FilePath, AnalysisResult)
-- Defined at src/Argon/Types.hs:29:10
instance ToJSON ComplexityBlock
-- Defined at src/Argon/Types.hs:17:10
In the expression: type .= result
In the first argument of ‘object’, namely
‘[path .= p, type .= result, blocks .= rs]’
In the expression:
object [path .= p, type .= result, blocks .= rs]
/home/miki/exp/argon/src/Argon/Types.hs:35:49:
No instance for (Data.String.IsString a1)
arising from the literal ‘result’
The type variable ‘a1’ is ambiguous
Note: there are several potential instances:
instance Data.String.IsString Value
-- Defined in ‘aeson-0.8.0.2:Data.Aeson.Types.Internal’
instance (a ~ Data.ByteString.Internal.ByteString) =>
Data.String.IsString
(attoparsec-0.12.1.6:Data.Attoparsec.ByteString.Internal.Parser a)
-- Defined in ‘attoparsec-0.12.1.6:Data.Attoparsec.ByteString.Char8’
instance Data.String.IsString
Data.ByteString.Builder.Internal.Builder
-- Defined in ‘Data.ByteString.Builder’
...plus four others
In the second argument of ‘(.=)’, namely ‘result’
In the expression: type .= result
In the first argument of ‘object’, namely
‘[path .= p, type .= result, blocks .= rs]’
I don't understand why the type variables are ambiguous.
Upvotes: 2
Views: 503
Reputation: 1918
Compare the signatures:
fromString :: IsString a => String -> a
toJSON :: ToJSON a => a -> Value
Technically, to serialize a string literal, your custom instance uses their superposition:
toJSON . fromString :: (IsString a, ToJSON a) => String -> Value
Note how the type variable disappears from the signature, so the ambiguity arises. For example, "error"
can happily be String
, Value
or Text
, all of which have both IsString
and ToJSON
instances.
A quick workaround is to resolve the ambiguity manually, by providing the explicit type signature:
toJSON (p, Left err) = object [ "path" .= p
, "type" .= ("error" :: String)
Upvotes: 4