如何在Haskell中将参数化类型序列化为JSON?

时间:2014-11-02 22:49:07

标签: haskell aeson

假设您有一个简单类型,并希望使用Aeson将其序列化为JSON。以下是基本设置:

{-# LANGUAGE DeriveGeneric #-}

import Data.Aeson (ToJSON)
import Data.Aeson (encode)
import GHC.Generics

data Spotting = Spotting {
  state :: String,
  bird :: String
} deriving (Show, Generic)

instance ToJSON Spotting

现在,除了birdstate字段之外,您还希望允许用户传入额外/自定义元数据。对于鸟类的观察,也许这是气温,鸟类的密度,潮汐的位置......可能是任何事情,我们事先都不知道。

通过查看Twitter API in Haskell等示例,您似乎想要像这样构建它:

data Spotting meta = Spotting {
  state :: String,
  bird :: String,
  meta :: meta
} deriving (Show, Generic)

instance ToJSON meta => ToJSON (Spotting meta)

根据我的理解,到目前为止,这是参数化类型。现在的目标是创建一个从该对象中创建一些JSON的简单方法。所以我们定义一个这样的函数:

spotting bird state meta = encode $ Spotting {
  state = state,
  bird = bird,
  meta = meta
}

但我不确定从这里开始。当我这样称呼这个函数时:

record = spotting "Snowy Egret" "California" "low tide"

它抛出一个错误(我是Haskell的新手,所以我仍在学习如何解释所有这些东西的基础知识)

No instance for (Data.String.IsString meta0)
  arising from the literal `"low tide"'
The type variable `meta0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
  instance Data.String.IsString
             aeson-0.7.0.6:Data.Aeson.Types.Internal.Value
    -- Defined in `aeson-0.7.0.6:Data.Aeson.Types.Internal'
  instance a ~ Data.ByteString.Internal.ByteString =>
           Data.String.IsString
             (attoparsec-0.12.1.2:Data.Attoparsec.ByteString.Internal.Parser a)
    -- Defined in `Data.Attoparsec.ByteString.Char8'
  instance Data.String.IsString Data.Text.Internal.Text
    -- Defined in `Data.Text'
  ...plus five others
In the third argument of `spotting', namely `"low tide"'
In the expression:
  spotting "Snowy Egret" "California" "low tide"
In an equation for `record':
    record = spotting "Snowy Egret" "California" "low tide"

这里发生了什么/你如何让它发挥作用?

最终目标是,不是传递meta字段中的字符串,而是传入一个类型化的对象(但它可以是任何对象),例如:

record = spotting "Snowy Egret" "California" MyCustomData {
  tide = "low"
}

你如何在Haskell中实现这一目标?

1 个答案:

答案 0 :(得分:6)

问题是因为你打开了OverloadedStrings ....

键入

record = spotting "Snowy Egret" "California" "low tide"

编译器需要确定meta的类型。因为encode能够解析许多类型,所以告诉编译器使用什么没有任何帮助。如果没有OverloadedStrings,"low tide"显然是String类型,但有了它,它可以是StringByteString等。

你有两种选择来解决这个问题....

要么 -

删除OverloadedStrings

或 -

明确指定类型。

value = "low tide"::String
record = spotting "Snowy Egret" "California" value