Type Classe实例的列表

时间:2017-09-29 21:20:21

标签: haskell functional-programming ghc functional-dependencies

我一直在玩Haskell类型课程,我遇到了一个问题,我希望有人可以帮我解决。考虑一下,我来自Swift背景,并且"尝试"将一些面向协议的知识移植到Haskell代码中。

最初我宣布了一堆具有相同结构的JSON解析器,只是一个不同的实现:

data Candle = Candle {
  mts :: Integer,
  open :: Double,
  close :: Double
}

data Bar = Bar {
  mts :: Integer,
  min :: Double,
  max :: Double
}

然后我决定创建一个" Class"这将定义他们的基本操作:

class GenericData a where
  dataName :: a -> String
  dataIdentifier :: a -> Double
  dataParsing :: a -> String -> Maybe a
  dataEmptyInstance :: a


instance GenericData Candle where
  dataName _ = "Candle"
  dataIdentifier = fromInteger . mts
  dataParsing _ = candleParsing
  dataEmptyInstance = emptyCandle

instance GenericData Bar where
  dataName _ = "Bar"
  dataIdentifier = fromInteger . mts
  dataParsing _ = barParsing
  dataEmptyInstance = emptyBar

我的第一个代码味道是需要包含" a"当它不需要时(dataNamedataParsing)但我接受了。

analyzeArguments :: GenericData a => [] -> [String] -> Maybe (a, [String])
analyzeArguments [] _             = Nothing
analyzeArguments _ []             = Nothing
analyzeArguments name data
    | name == "Candles" = Just (head possibleCandidates, data)
    | name == "Bar" = Just (last possibleRecordCandidates, data)
    | otherwise  = Nothing

possibleCandidates :: GenericData a => [a]
possibleCandidates = [emptyCandle, emptyBar]

现在,当我想选择是否应该选择任一个实例来执行解析时,我总是会收到以下错误

• Couldn't match expected type ‘a’ with actual type ‘Candle’
  ‘a’ is a rigid type variable bound by
    the type signature for:
      possibleCandidates :: forall a. GenericData a => [a]
    at src/GenericRecords.hs:42:29

我的目标是创建GenericData的实例列表,因为其他函数依赖于被选中来执行正确的dataParser。我知道这与类型类检查器* -> Constraint有关,但仍未找到解决此冲突的方法。我使用了几种GHC语言扩展,但没有一种解决了这个问题。

1 个答案:

答案 0 :(得分:2)

你有一个类型签名:

possibleCandidates :: GenericData a => [a]

你可能意味着你可以在该列表中放置任何内容,只要它是GenericData。但这不是Haskell的类型系统实际工作的方式。值possibleCandidates可以是具有GenericData类的任何类型的列表,但列表的每个元素必须是相同的类型。

GHC错误消息告诉你(以它自己的特殊方式)是列表的第一个元素是Candle所以它认为列表的其余部分也应该是{{1}类型但是第二个元素实际上是Candle

现在有很多方法可以在Haskell中创建异构列表(和其他集合),但它几乎不是正确的事情。

此问题的一个典型解决方案是将所有内容合并为一个sum data type

Bar

您甚至可以放弃间接步骤,只需将Candle和Bar数据直接放入数据结构中。

现在改为一个类,你只有一个数据类型,你的类函数变成了正常的函数:

data GenericData = GenericCandle Candle | GenericBar Bar

还有一些其他更复杂的方法可以使这项工作,但如果总和数据类型适合该法案,请使用它。 Haskell中的解析器通常具有大量数据类型(通常也是递归的)作为结果。有关示例,请查看Value标准JSON库中的Aeson类型。