I was working on the following small fragment of code:
import Control.Monad
import Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import Data.Map (Map)
import qualified Data.Map as Map
import GHC.Generics
-- definitions of Whitelisted, WhitelistComment and their FromJSON instances
-- omitted for brevity
data Whitelist = Whitelist
{ whitelist :: Map Whitelisted WhitelistComment
} deriving (Eq, Ord, Show)
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . forM (HashMap.toList v) $ \(a, b) -> do
a' <- parseJSON (String a)
b' <- parseJSON b
return (a', b')
parseJSON _ = mzero
when I realised that I can rewrite the do
block in applicative style:
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . forM (HashMap.toList v) $ \(a, b) ->
(,) <$> parseJSON (String a) <*> parseJSON b
parseJSON _ = mzero
and with that I could also replace forM
with for
. Before making the change above I switched to for
first:
instance FromJSON Whitelist where
parseJSON (Object v) =
fmap (Whitelist . Map.fromList) . for (HashMap.toList v) $ \(a, b) -> do
a' <- parseJSON (String a)
b' <- parseJSON b
return (a', b')
parseJSON _ = mzero
and to my surprise this still compiled. Given the definition of for
:
for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
I thought the Applicative
constraint would prevent me from using do notation / return in the action passed to for
.
I'm clearly missing something fundamental here, either in terms of what for
signature really implies, or how the code I posted is interpreted by the compiler, and would appreciate any help understanding what's going on.