首页 > 解决方案 > 值在 Haskell 中被丢弃

问题描述

我不明白为什么会这样:

module Main where

import Control.Monad.Reader

data Config = Config Int

getA :: (Monad m) => ReaderT Config m Int
getA = do
  Config a <- ask
  return a

readConfig :: Config -> IO Int
readConfig = runReaderT getA

main :: IO ()
main = do
  print "Start"
  let a = Config 2
  b <- readConfig a -- Will be printed
  print b
  let c = Config 4
  print <$> readConfig c -- Silence, nobody complains..
  print "Done"

结果就是:

"Start"
2
"Done"

为什么是print <$> readConfig a空的?即使-Wall没有人抱怨这...

(我在 Godbolt 中尝试了 diff ghc 版本,但结果保持不变)

编辑: 好的,我找到了问题的等效描述,感谢@chi和@David:

module Main where
test :: IO ()
test = do 
    print "test"
    return ()

main :: IO ()
main = do
  let t = test -- equivalent to print <$> readConfig c, is IO (IO ())
  test -- equivalent to print b is IO ()

现在,我只想知道为什么let t不评估。从我的直觉来看,以前很明显,但现在已经不明显了……但是对此有答案(尤其是来自 Tanner Swett

标签: haskellghc

解决方案


GHC 8.10.3 确实用 -Wall 抱怨了这一点(在我在你的代码中更改initConfig为之后):readConfig

一个 do-notation 语句丢弃了 'IO ()' 类型的结果<br /> 通过说 '_ <- print <$> readConfig c'</p> 抑制此警告

你有readConfig ctype IO Int。然后你 fmap print,给出一个类型为IO (IO ()). 由于结果没有绑定到 do 块中的变量,因此它被丢弃了。

在 fmap 的类型中,Functor f => (a -> b) -> f a -> f b我们有fbeing IOabeingIntbbeing IO ()。所以它与 type 一起使用(Int -> IO ()) -> IO Int -> IO (IO ())

这会起作用:

printAction <- print <$> readConfig c
printAction

但你真正想要的是绑定:

print =<< readConfig c

推荐阅读