我一直在玩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"当它不需要时(dataName
或dataParsing
)但我接受了。
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语言扩展,但没有一种解决了这个问题。
答案 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类型。