这是this question的后续问题,涉及在单子上下文中解析JSON。
考虑一个简单的记录类型及其Data.Aeson.FromJSON实例。对于(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")
这很好:
> (decodeThrow "{\"name\": \"Greenland\", \"reading\": 54}") :: Maybe DataPoint
Just (DataPoint {placeName = "Greenland", readings = (54,20)})
我想用单独提供的值代替那个假人20。在Daniel Wagner’s answer之后,我可以将其提升为reader monad / environment functor (->) 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")
在GHCi中,
> let f = (decodeThrow "{\"name\": \"Greenland\", \"reading\": 54}") :: Maybe (Int -> DataPoint)
> f <*> Just 7
Just (DataPoint {placeName = "Greenland", readings = (54,7)})
在这种情况下,liftA2
的类型为
liftA2 :: Functor f => (a -> b -> c) -> f a -> f b -> f c
专门用于
liftA2 :: (Parser String -> Parser (Int, Int) -> Parser DataPoint)
-> (Int -> Parser String)
-> (Int -> Parser (Int, Int))
-> (Int -> Parser DataPoint)
这是我的问题。我的真实记录有七个字段,因此我必须编写一个liftA7
之类的函数,才能对我的真实数据使用相同的方法。这很容易,但是标准库仅提供liftA
,liftA2
和liftA3
的事实使我认为我应该使用另一种方法将解析器放入{ {1}}函子。有没有更好的方法?还是我只需要写出(->) Int
并使用它?
答案 0 :(得分:4)
这里无需遍历嵌套上下文。类型arrange
只是要求您编写一个返回函数的foo <- foo %>%
arrange(ID) %>%
group_by(Heading) %>%
mutate(Count = seq_len(n()))
。
查看您的原始代码
parseJSON :: Parser (Int -> DataPoint)
首先,我注意到您在Parser
调用下面使用了嵌套的parseJSON = withObject "DataPoint" $ \o -> DataPoint
<$> o .: "name"
<*> fmap (\v -> (v, 20)) (o .: "reading")
。我将重新关联此代码(此合法性为a consequence of the applicative laws)以简化您的代码:
fmap
我认为,这更容易阅读。我只是将两个参数的函数应用于上下文中的两个参数。
现在希望您可以看到如何使此解析器返回<*>
-只需更改要映射到参数的函数的返回类型即可。
parseJSON = withObject "DataPoint" $ \o ->
(\name reading -> DataPoint name (reading, 20))
<$> o .: "name"
<*> o .: "reading"
从语法上讲,Haskell允许您展平嵌套的lambda。
Int -> DataPoint
顺便说一句,这都等同于嘈杂但也许更清晰的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"
快速测试:
do