Reputation: 314
Here's the background, in order to build a JSON parser:
We have a data type JValue
and several type constructors of that one, for example, JNumber
, which takes one numeric value and construct a JValue
data.
data JValue = JNumber Double deriving (Eq, Ord, Show)
Now we want to build a error handler which could check if the input parameter could be parsed. Here's a typeclass definition:
type JSONError = String
class JSON a where
fromJValue :: JValue -> Either JSONError a
Then implement the instance of class JSON:
instance JSON JValue where
fromJValue = Right
Here's the problem, I want to try fromJValue
with (JNumber 1)
which should be a JValue
type variable. However, in ghci, I have this:
*Main> fromJValue (JNumber 1)
<interactive>:3:1:
No instance for (JSON a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
Note: there is a potential instance available:
instance JSON JValue -- Defined at JSONSubset.hs:8:10
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it
According to this error info, it's the function print
that can't recognize the type of return value of fromJValue (JNumber 1)
. That's the confusing point: I think the result of fromJValue
could be deduced as Either JSONError JValue
as it's a member of instance JSON JValue
.
Note: About the meaning of fromJValue
, I think what the author tried to do is implementing the Int
or String
instance of class JSON
, then we could get the content of JValue
.
Upvotes: 0
Views: 255
Reputation: 54058
From my comment
Look at how aeson implements it, you have
decode :: FromJSON a => ByteString -> Maybe a
Whenever you use it interactively, you always have to specify it as
decode "{}" :: Maybe MyType
When you're using fromJValue
(or decode
) in larger projects, the majority of the time those polymorphic values get converted into concrete ones at some point, so the compiler can infer what type to use, but when used interactively you have to have something that is making the type concrete before you can print it. There are exceptions to this rule, in that GHCi has defaults for Num
, Integral
and a few others simply because without these defaults there would be a lot more type errors and confusion.
Upvotes: 1
Reputation: 52039
The problem is that fromJSON (JValue 1)
has type Either String a
for some type a
, but you haven't specified what a
is. GHC says that it knows about one particular instance, but it's not going to assume that that's the one you mean.
Here are some more examples of more possible instances of the JSON
type class:
import qualified Data.ByteString.Char8 as B
-- convert to a Double
instance JSON Double where
fromJValue (JNumber x) = Right x
-- convert to a ByteString
instance JSON B.ByteString where
fromJValue (JNumber x) = Right (B.pack $ show x)
-- convert to an Int
instance JSON Int where
fromJValue (JNumber x) = Left "conversion to Int not supported"
and some examples of using these instances:
ghci> let j = JNumber 123
ghci> fromJValue j :: Either String B.ByteString
Right "123.0"
ghci> fromJValue j :: Either String Double
Right 123.0
ghci> fromJValue j :: Either String Int
Left "conversion to Int not supported"
Without a way to infer the type of a
, the interpretation of fromJSON j
is ambiguous.
Upvotes: 1
Reputation: 2205
To make your code compile, just change your class definition to:
class JSON a where
fromJValue :: a -> Either JSONError a
Upvotes: 0