实例减速中的类型变量 (Aeson)

Type Variables in Instance Decelerations (Aeson)

我有 JSON 可能看起来像这样的数据

{
"items": [Day],
"pageCount": Int,
"totalCount": Int
}

或这个

{
"items": [Order],
"pageCount": Int,
"totalCount": Int
}

我一直在尝试为与 FromJSON 一起使用的不变字段创建一个数据类型,但在经历各种变化时我一直无法找到正确的方法的错误。这是我当前状态下的代码

--{-# LANGUAGE FlexibleInstances #-}
--{-# LANGUAGE MultiParamTypeClasses #-}
--{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
import           Data.Aeson

data Typed = Typed {typeID::Int,name::String} deriving (Show,Eq)
data Day =  Day {orderCount::Int,lowPrice::Float,highPrice::Float, avgPrice:: Float,volume::Int,date::String}
data Order = Order {price::Float,isBuy::Bool,location::Typed} deriving (Show,Eq)
data Market a = Market {items::a,pageCount::Int,totalCount::Int} deriving (Show,Eq)
-- Can be either Market [Order] or Market [Day]
instance FromJSON (Market a) where
  parseJSON (Object x) = Market <$> x .: "items" <*> x .: "pageCount" <*> x .: "totalCount"
instance FromJSON Order where
  parseJSON (Object x) = Order <$> x .: "price" <*> x .: "buy" <*> x .: "location"
instance FromJSON Typed where
  parseJSON (Object x) = Typed <$> x .: "id" <*> x .: "name"
instance FromJSON Day where
  parseJSON (Object x) = Day <$> x .: "orderCount" <*> x .: "lowPrice" <*> x .: "highPrice"
    <*> x .: "avgPrice" <*> x .: "volume" <*> x .: "date"

这是我得到的当前错误

No instance for (FromJSON a) arising from a use of ‘.:’
    Possible fix:
      add (FromJSON a) to the context of the instance declaration
    In the second argument of ‘(<$>)’, namely ‘x .: "items"’
    In the first argument of ‘(<*>)’, namely ‘Market <$> x .: "items"’
    In the first argument of ‘(<*>)’, namely
      ‘Market <$> x .: "items" <*> x .: "pageCount"’

所以这里有两个问题。第一个是编译错误:你声称你有一个 Market a 的实例,这应该意味着你知道如何为 a 的任何选择解析市场的 JSON 序列化,但是这个这实际上是不可能的,因为您想解析 a 类型的内容以便为 price 字段的内容定价。

我们将注意力限制在仅考虑我们知道如何通过向实例声明添加约束来解析的可能 a

instance FromJSON a => FromJSON (Market a) where
  ...

现在一切都很好。但是还有另一个问题,我们实现 FromJSON 的方式我们看到它充满了非穷尽的匹配!试试 运行 这个和 -Wall 看看 GHC 向我们抱怨它。现在的问题是,如果输入 Object 以外的内容,每个 parseJSON 实际上都会失败并出现异常(不是解析失败,如 "blow up the whole program" 失败)。这意味着我们会遇到像

这样的不良行为
λ> decode "1.0" :: Maybe Typed
*** Exception: /home/jozefg/scratch/Aeson.hs:(24,3)-(25,39): Non-exhaustive patterns in function parseJSON

为了解决这个问题,我们可以添加另一个在所有其他情况下显式失败的子句。更具生产性的解决方案可能是不手写这些实例,因为它们都非常简单,而是使用 aeson 对泛型的支持。这是 Order 的固定实例,例如我刚刚添加了一个额外的子句

instance FromJSON Order where
  parseJSON (Object x) =
    Order <$> x .: "price" <*> x .: "buy" <*> x .: "location"
  parseJSON _ = mempty