Haskell 将大型记录类型的解析器提升为函子
这是关于在一元上下文中解析JSON的后续问题 考虑一个简单的记录类型及其实例。对于Haskell 将大型记录类型的解析器提升为函子,haskell,Haskell,这是关于在一元上下文中解析JSON的后续问题 考虑一个简单的记录类型及其实例。对于(Int,Int)字段,我从JSON中读取一个值,并将另一个值设置为20 import Data.Aeson data DataPoint = DataPoint { placeName :: String , readings :: (Int, Int) } deriving (Show) instan
(Int,Int)
字段,我从JSON中读取一个值,并将另一个值设置为20
import Data.Aeson
data DataPoint = DataPoint { placeName :: String
, readings :: (Int, Int)
} deriving (Show)
instance FromJSON DataPoint where
parseJSON = withObject "DataPoint" $ \o -> DataPoint
<$> o .: "name"
<*> fmap (\v -> (v, 20)) (o .: "reading")
我想用一个单独提供的值替换虚拟的20。接下来,我可以将其提升到/(>)Int
中
import Control.Applicative (liftA2)
instance FromJSON (Int -> DataPoint) where
parseJSON = withObject "DataPoint" $ \o -> liftA2 DataPoint
<$> fmap pure (o .: "name")
<*> fmap (\v c -> (v, c)) (o .: "reading")
是专门为
liftA2 :: (Parser String -> Parser (Int, Int) -> Parser DataPoint)
-> (Int -> Parser String)
-> (Int -> Parser (Int, Int))
-> (Int -> Parser DataPoint)
这是我的问题。我的真实记录大约有七个字段,因此我必须编写一个类似于
liftA7
的函数,才能对我的真实数据使用相同的方法。这很简单,但标准库只提供liftA
、liftA2
和liftA3
这一事实让我认为应该使用不同的方法将解析器提升到(>)Int
functor中。有更好的方法吗?或者我需要写出liftA7
并使用它吗?这里不需要通过嵌套上下文进行提升。类型parseJSON::Parser(Int->DataPoint)
只是要求您编写一个返回函数的解析器
看看你的原始代码
parseJSON = withObject "DataPoint" $ \o -> DataPoint
<$> o .: "name"
<*> fmap (\v -> (v, 20)) (o .: "reading")
在我看来,总的来说,这更容易阅读。我只是对上下文中的两个参数应用一个双参数函数
现在,希望您能看到如何使这个解析器返回一个Int->DataPoint
——只需更改映射到参数的函数的返回类型即可
parseJSON = withObject "DataPoint" $ \o ->
(\name reading -> \x -> DataPoint name (reading, x))
<$> o .: "name"
<*> o .: "reading"
快速测试:
main =
let input = "{\"name\": \"Greenland\", \"reading\": 54}"
f = decode input :: Maybe (Int -> DataPoint)
in print $ fmap ($ 7) f
-- Just (DataPoint {placeName = "Greenland", readings = (7,54)})
看起来您可能需要Data.Functor.Compose
,这样您就可以使用普通的应用程序语法和一个两层的Functor。您能详细介绍一下应用程序规则如何允许您将fmap(\v->(v,20))
浮动到“顶部”吗?@chepner,但直觉上,fmap
不会产生任何效果,所以你可以随意移动它。绝对不明显:)我认为这是值得添加的,作为答案底部的附录。@BenjaminHodgson我采用了这种方法,并围绕它写了一篇博文:再次感谢您在这里分享这一技巧!很乐意帮忙!❤️
parseJSON = withObject "DataPoint" $ \o -> DataPoint
<$> o .: "name"
<*> fmap (\v -> (v, 20)) (o .: "reading")
parseJSON = withObject "DataPoint" $ \o ->
(\name reading -> DataPoint name (reading, 20))
<$> o .: "name"
<*> o .: "reading"
parseJSON = withObject "DataPoint" $ \o ->
(\name reading -> \x -> DataPoint name (reading, x))
<$> o .: "name"
<*> o .: "reading"
parseJSON = withObject "DataPoint" $ \o ->
(\name reading x -> DataPoint name (reading, x))
<$> o .: "name"
<*> o .: "reading"
parseJSON = withObject "DataPoint" $ \o -> do
name <- o .: "name"
reading <- o .: "reading
return $ \x -> DataPoint name (reading, x)
main =
let input = "{\"name\": \"Greenland\", \"reading\": 54}"
f = decode input :: Maybe (Int -> DataPoint)
in print $ fmap ($ 7) f
-- Just (DataPoint {placeName = "Greenland", readings = (7,54)})