fff :: Int -> Maybe Int
fff i = do
(+10)
<$> Just i
Why is the above considered valid syntax?
Because it is parsed as
fff i = do { -- do { A } is just
(+10) } -- A
<$> Just i
which is equivalent to
fff i =
(+10)
<$> Just i
because <$> Just i
on its own is an invalid expression (so fff i = ((+10) >>= (\i -> fmap (Just i))) i
is incorrect translation), and that delimits the extent of the do
block as per the rule quoted in @chi's answer.
Indeed its type is inferred as
fff :: Num b => b -> Maybe b
You second example works if you add a space before the <$>
in the last line. Without the space, it is again parsed as
inputTest :: FormInput -> IO (Either [String] (Int, Int))
inputTest fi = do {
allErrors' <- undefined :: IO [String]
undefined }
<$> ((liftM2 ) (,) <$> undefined <*> undefined) fi
because <$> ...
on its own is invalid expression. Indeed when I add the explicit separators,
inputTest2 :: String -> IO (Either [String] (Int, Int))
inputTest2 fi = do {
allErrors2 <- undefined :: IO [String] ;
undefined }
<$> ((liftM2 ) (,) <$> undefined <*> undefined) fi
I get the exact same error message on TIO (had to use String
instead of your type there).
Since the first undefined :: IO [String]
, the whole do block has some IO t
type, and we can't fmap that over anything.
Always add all the explicit separators (in addition to practicing good indentation style), to avoid this weird syntax brittleness.
Your new example is
x :: Maybe Int
x = do -- { this is
_ <- Just 1 -- ; how it is
undefined -- } parsed
<$> Just 5
The code changed, but the answer is the same. The do
block before the <$>
is Maybe t
(because of Just 1
), and we can't fmap that.
Again, indent the last line some more and it'll compile, because undefined <$> Just 5
will now be parsed as one expression.