Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 将大型记录类型的解析器提升为函子_Haskell - Fatal编程技术网

Haskell 将大型记录类型的解析器提升为函子

Haskell 将大型记录类型的解析器提升为函子,haskell,Haskell,这是关于在一元上下文中解析JSON的后续问题 考虑一个简单的记录类型及其实例。对于(Int,Int)字段,我从JSON中读取一个值,并将另一个值设置为20 import Data.Aeson data DataPoint = DataPoint { placeName :: String , readings :: (Int, Int) } deriving (Show) instan

这是关于在一元上下文中解析JSON的后续问题

考虑一个简单的记录类型及其实例。对于
(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)})