中缀形式的多个运算符的评估顺序

时间:2014-07-27 16:05:24

标签: haskell

鉴于此:

 data Base = Base {
   key1 :: Text,
   key2 :: Text,
   key3 :: Text
 } deriving (Show)

instance FromJSON Base where
  parseJSON (Object v) = Base <$>
                         ((v .: "base123") >>= (.: "key1")) <*>  -- 1
                         ((v .: "base123") >>= (.: "key2")) <*>  -- 2
                         ((v .: "base123") >>= (.: "key3"))      -- 3

  parseJSON _ = mzero

中缀运算符<$><*><*>的应用顺序是什么?换句话说,如果我以前缀形式重写它:

instance FromJSON Base where
      parseJSON (Object v) = Base <$> ((<*>) ((v .: "base123") >>= (.: "key1")) $ (<*>) ((v .: "base123") >>= (.: "key2")) ((v .: "base123") >>= (.: "key3")))

      parseJSON _ = mzero

(注意$运算符),第一个<*>的右边部分是否会被首先评估,因为只有在这种情况下才有意义,因为第一个<*>需要2个参数?由于它需要2个参数,我们还必须使用$

我可能会问我的问题,因此很难理解我的意思,但我希望你明白。

1 个答案:

答案 0 :(得分:2)

实际上你的前缀形式不太正确,它应该是这样的:

parseJSON (Object v) = ((<*>)
                        ((<*>)
                         ((<$>) Base ((v .: "base123") >>= (.: "key1")))
                         (((v .: "base123") >>= (.: "key2"))))
                        (((v .: "base123") >>= (.: "key3"))))

上述定义仍然没有完整的前缀形式。您必须将>>=.:向左移动才能使它们成为完整的前缀。话虽如此,为了找到以中缀形式评估多个运算符的确切顺序,我建议你在ghci中进行操作以获得更多关于类型的见解。作为初始步骤,检查所有运算符的关联性和优先顺序:

λ> :i (<$>)
(<$>) :: Functor f => (a -> b) -> f a -> f b
infixl 4 <$>
λ> :i (<*>)
(<*>) :: f (a -> b) -> f a -> f b
infixl 4 <*>

因此,它们都是关联的并具有相同的优先级。定义的中缀形式非常清楚评估将如何进行:它们从左侧开始,最初<$>应用于Base,然后应用两个<*>函数。类型Base最初应用于<$>

λ> :t Base
Base :: Text -> Text -> Text -> Base
λ> :t (Base <$>)
(Base <$>) :: Functor f => f Text -> f (Text -> Text -> Base)

现在,((v .: "base123") >>= (.: "key1"))已应用于上述类型的结果:

λ> let (Object v) = undefined :: Value
λ> :t (Base <$> ((v .: "base123") >>= (.: "key1")))
(Base <$> ((v .: "base123") >>= (.: "key1"))) :: Parser (Text -> Text -> Base)

您可以看到它返回包含在Parser类型中的函数。要从Parser类型中提取基础函数,您必须使用<*>

λ> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
λ> :t (Base <$> ((v .: "base123") >>= (.: "key1")) <*>)
(Base <$> ((v .: "base123") >>= (.: "key1")) <*>) :: Parser Text -> Parser (Text -> Base)

您可以按照类似的步骤查看它如何应用于函数定义的其他部分。最后,您将获得Parser Base类型。