2016-12-17 2 views
2

Essayant de faire le JSON de-sérialisation pour un type de données avec TypeLits, je reçois coincé avec le problème suivant:exemple FromJSON avec DataKinds

 
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) 

Comment serait la syntaxe correcte pour permettre Nat génériquement en le FromJSON exemple dans l'exemple suivant:

{-# 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

Vous avez probablement besoin d'instances séparées pour 'FromJSON (X 1)' et 'FromJSON (X 2)', ou faites une typecase sur 'n' (en utilisant un singleton). La façon dont vous avez écrit l'instance indique que vous pouvez lire «X n» pour tout «n» que l'appelant * choisit. – luqui

Répondre

1

Comme vous le savez peut évidemment pas le type que vous allez désérialiser au moment de la compilation, le type exact doit être caché dans un existentiel, puis restauré via la correspondance de modèle. J'utilise généralement un type générique Some pour masquer les types fantômes.

{-# LANGUAGE PolyKinds #-} 

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

Maintenant, vous pouvez écrire l'instance comme

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 

Cependant, vous devez également activer l'extension FlexibleInstances.

+0

Vous pouvez éviter 'FlexibleInstances' en utilisant un' data SomeX = forall n spécialisé. SomeX (X n) ', si vous voulez. Qu'est-ce que la version générique vous gagnez? – dfeuer

+0

Rien dans cet exemple simple, c'est sûr. Cependant, je voulais inclure la version plus générique puisque vous avez habituellement plusieurs types différents dans une application à partir de laquelle vous voulez cacher temporairement le paramètre type. – shang