Vladimir
Vladimir

Reputation: 541

ApplicativeDo pragma and Applicative Functor in Haskell

An example from Functor, Applicative, and Monad slightly changed:

{-# 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

where maybeAge with do I used instead of

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

I have the 2 questions:

  1. How can I check does maybeAge use Applicative Functor semantic or Monad one in this case?
  2. What is an advantage in that case if Applicative Functor used?

About: ApplicativeDo.

Upvotes: 1

Views: 168

Answers (1)

Artem Pelenitsyn
Artem Pelenitsyn

Reputation: 2578

I made a self-contained example out of yours:

{-# 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

Also, in the last line, I swapped the arguments, as they appear to be in the wrong order in your example. Also I improved yearDif definition as per @Redu's comment.

Here are the answers on your questions.

  1. You can check that applicative (and functor) operations are indeed applied, following the advice in the GHC's User Guide, namely, using the -ddump-ds compiler switch. I add a couple more switches below to make the output more succinct. I also show only excerpt concerning the maybeAge function.

    $ 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. Most certainly, no speedup is gained here. Applicative opertaions for Maybe have constant complexity (O(1)) — just like the monadic ones.

    In the original paper, the authors of ApplicativeDo give several examples of more sophisticated monadic types (Haxl, Data.Seq, parsing, etc.) allowing for asymptotically more efficient applicative operations. See Section 6 of the paper.

Upvotes: 2

Related Questions