VELVETDETH
VELVETDETH

Reputation: 314

Haskell -- type signature on variable or function

Here's the background, in order to build a JSON parser:

  1. 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)
    
  2. 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
    
  3. 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

Answers (3)

bheklilr
bheklilr

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

ErikR
ErikR

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

jsalvata
jsalvata

Reputation: 2205

To make your code compile, just change your class definition to:

class JSON a where
    fromJValue :: a -> Either JSONError a

Upvotes: 0

Related Questions