fff :: Int -> Maybe Int
fff i = do
(+10)
<$> Just i
Почему указанный выше допустимый синтаксис?
Поскольку он анализируется как
fff i = do { -- do { A } is just
(+10) } -- A
<$> Just i
, что эквивалентно
fff i =
(+10)
<$> Just i
, поскольку <$> Just i
само по себе является недопустимым выражением (поэтому fff i = ((+10) >>= (\i -> fmap (Just i))) i
является неправильным переводом), и это ограничивает экстент блока do
согласно правилу, указанному в ответе @ chi.
Действительно, его тип выводится как
fff :: Num b => b -> Maybe b
Ваш второй пример работает, если добавить пробел перед <$>
в последней строке. Без пробела он снова анализируется как
inputTest :: FormInput -> IO (Either [String] (Int, Int))
inputTest fi = do {
allErrors' <- undefined :: IO [String]
undefined }
<$> ((liftM2 ) (,) <$> undefined <*> undefined) fi
, поскольку само по себе <$> ...
является недопустимым выражением. Действительно, когда я добавляю явные разделители,
inputTest2 :: String -> IO (Either [String] (Int, Int))
inputTest2 fi = do {
allErrors2 <- undefined :: IO [String] ;
undefined }
<$> ((liftM2 ) (,) <$> undefined <*> undefined) fi
Я получаю точно такое же сообщение об ошибке на TIO (пришлось использовать String
вместо вашего типа).
Начиная с первого undefined :: IO [String]
, весь блок do имеет некоторое IO t
введите, и мы не можем fmap , что поверх чего-либо.
Всегда добавляйте все явные разделители (в дополнение к практике хорошего стиля отступов), чтобы избежать этой странной хрупкости синтаксиса.
Ваш новый пример:
x :: Maybe Int
x = do -- { this is
_ <- Just 1 -- ; how it is
undefined -- } parsed
<$> Just 5
Код изменился, но ответ тот же. do
блок до <$>
равен Maybe t
(из-за Just 1
), и мы не можем fmap , что .
Опять же, сделайте отступ в последней строке еще немного, и он скомпилируется, потому что undefined <$> Just 5
теперь будет анализироваться как одно выражение.