3

Trying to do the JSON de-serialisation for a data type with TypeLits, I get stuck with the following problem:

Couldn't match type ‘n’ with ‘2’
      ‘n’ is a rigid type variable bound by
        the instance declaration at test.hs:14:10
      Expected type: aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser
                       (X n)
        Actual type: aeson-0.11.2.1:Data.Aeson.Types.Internal.Parser
                       (X 2)

How would be the correct syntax to allow Nat generically in the FromJSON instance in the following example:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

import GHC.TypeLits
import Data.Aeson
import Control.Monad (mzero)

data X (n :: Nat) where
  A :: Integer -> X 1
  B :: Integer -> X 2

instance FromJSON (X n) where
  parseJSON (Object o) = do
      v <- o .: "val"
      t <- o .: "type"
      case t of
        "a" -> return $ A v
        "b" -> return $ B v
  parseJSON _       = mzero
1
  • 1
    You probably need separate instances for FromJSON (X 1) and FromJSON (X 2), or do a typecase on n (using a singleton). The way you have the instance written says that you can read X n for any n the caller chooses. Commented Dec 17, 2016 at 18:58

1 Answer 1

4

Since you obviously cannot know the type you are going to deserialize at compile time, the exact type needs to be hidden in an existential and then restored via pattern matching. I usually use a generic Some type to hide phantom types.

{-# LANGUAGE PolyKinds #-}

data Some (t :: k -> *) where
    Some :: t x -> Some t

Now you can write the instance as

instance FromJSON (Some X) where
    parseJSON (Object o) = do
        v <- o .: "val"
        t <- o .: "type"
        case (t :: String) of
          "a" -> return $ Some $ A v
          "b" -> return $ Some $ B v
    parseJSON _       = mzero

However, you also need to enable the FlexibleInstances extension.

Sign up to request clarification or add additional context in comments.

2 Comments

You can avoid FlexibleInstances by using a specialized data SomeX = forall n . SomeX (X n), if you like. What does the generic version win you?
Nothing in this simple example, for sure. However, I wanted to include the more generic version since you usually have several different types in an application from which you want to temporarily hide the type parameter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.