Haskell中的ApplicativeDo编译指示和Applicative函子

时间:2018-12-09 06:20:19

标签: haskell monads functor applicative

Functor, Applicative, and Monad 中的一个示例稍有变化:

{-# LANGUAGE ApplicativeDo #-}

import Safe (readMay)
-- import Control.Applicative ((<$>), (<*>))

displayAge maybeAge =
    case maybeAge of
        Nothing -> putStrLn "You provided invalid input"
        Just age -> putStrLn $ "In that year, you will be: " ++ show age

yearDiff futureYear birthYear = futureYear - birthYear

maybeAge fS bS = do 
   fI <- readMay fS
   bI <- readMay bS
   pure $ yearDiff fI bI       

main = do
    putStrLn "Please enter your birth year"
    birthYearString <- getLine
    putStrLn "Please enter some year in the future"
    futureYearString <- getLine
    displayAge $ maybeAge birthYearString futureYearString

我使用maybeAgedo代替

maybeAge fS bS = yearDiff <$> readMay fS <*> readMay bS

我有两个问题:

  1. 在这种情况下,如何检查maybeAge是否使用Applicative Functor语义或Monad?
  2. 如果使用Applicative Functor,在这种情况下有什么优势?

关于:ApplicativeDo

1 个答案:

答案 0 :(得分:2)

我从您身上做了一个完整的例子:

{-# LANGUAGE ApplicativeDo #-}

import Text.Read (readMaybe)

displayAge :: Maybe Int -> IO ()
displayAge maybeAge =
    case maybeAge of
        Nothing -> putStrLn "You provided invalid input"
        Just age -> putStrLn $ "In that year, you will be: " ++ show age

yearDiff :: Int -> Int -> Int
yearDiff  = (-)

maybeAge :: String -> String -> Maybe Int
maybeAge fS bS = do 
   fI <- readMaybe fS
   bI <- readMaybe bS
   pure $ yearDiff fI bI

main :: IO ()
main = do
    putStrLn "Please enter your birth year"
    birthYearString <- getLine
    putStrLn "Please enter some year in the future"
    futureYearString <- getLine
    displayAge $ maybeAge futureYearString birthYearString

此外,在最后一行,我交换了参数,因为它们在示例中的顺序似乎不正确。另外,我根据@Redu的评论改进了yearDif的定义。

这是您问题的答案。

  1. 您可以按照GHC User Guide中的建议,即使用-ddump-ds编译器开关,检查是否确实应用了应用(和函子)操作。我在下面添加了几个开关,以使输出更简洁。我还仅显示有关maybeAge函数的摘录。

    $ ghc appdo.hs -ddump-ds -dsuppress-type-applications -dsuppress-module-prefixes 
    [1 of 1] Compiling Main             ( appdo.hs, appdo.o )
    
    ==================== Desugar (after optimization) ====================
    Result size of Desugar (after optimization)
      = {terms: 75, types: 75, coercions: 0, joins: 0/0}
    ...
    -- RHS size: {terms: 17, types: 13, coercions: 0, joins: 0/0}
    maybeAge :: String -> String -> Maybe Int
    [LclId]
    maybeAge
      = \ (fS_a1h3 :: String) (bS_a1h4 :: String) ->
          <*>
            $fApplicativeMaybe
            (fmap
               $fFunctorMaybe
               (\ (fI_a1h5 :: Int) (bI_a1h6 :: Int) -> yearDiff fI_a1h5 bI_a1h6)
               (readMaybe $fReadInt fS_a1h3))
            (readMaybe $fReadInt bS_a1h4)
    ...
    
  2. 最肯定的是,这里没有加速。 Maybe的适用操作符具有恒定的复杂度(O(1)),就像单子步操作一样。

    original paper中,ApplicativeDo的作者给出了一些更复杂的单子类型(HaxlData.Seq,解析等)的示例,从而使渐近效率更高应用操作。请参阅本文的第6节。