Haskell: GHC为主方法推断的类型为什么不完整?

3
例如,拿Don Stewart回答Stack Overflow问题的代码来说:
import Control.Monad
import qualified Data.HashTable as H
import System.Environment

main = do
  [size] <- fmap (fmap read) getArgs
  m <- H.new (==) H.hashInt
  forM_ [1..size] $ \n -> H.insert m n n
  v <- H.lookup m 100
  print v

在 GHCi 中加载它。

:t getArgs ---> getArgs :: IO [String]
:t main    ---> main :: IO ()

为什么 main 函数的类型签名不反映调用了 getArgs :: IO [String] 函数?

当你运行二进制文件时,可以传递一个参数。 <prog> 145 返回 Just 100。 但在 GHCi 中无法这样做:main 145 报错。如何在 GHCi 中运行此程序并传递参数。

2个回答

6
< p > main 的类型与其最终表达式相同;print 生成 IO (),因此这是 main 的类型。中间类型不相关,因为 (>>=) 不会传播除了 monad 以外的任何东西。

(>>=) :: Monad m => m a -> (a -> m b) -> m b

a在结果类型 (m b) 中没有出现。

至于在GHCi中运行您的程序,请查看:main命令。


谢谢。我明白 do 是 >>= 的语法糖,它的签名是 (>>=) :: (Monad m) => m a -> (a -> m b) -> m b。然而,即使使用了单子操作,编译器是否不能利用 getArgs 的类型签名为 getArgs :: IO [String] 被用于计算这一事实?并提供一个更有意义的类型签名?或者根本不可能吗? - Babu Srinivasan
不是在 Haskell 中,我也不清楚除了已经存在的 IO 之外,这样做会有什么价值。这是对编译器有帮助的信息,但除此之外并没有其他用处。除非你真正考虑的是拆分 IO,这是一个长期讨论的话题,最终结果是这将是一场后勤噩梦。 - geekosaur
2
如果这让你感到困扰,那么只需将所有代码放入一个 mainX :: [String] -> IO () 函数中,并使用 main = getArgs >>= mainX - Thomas M. DuBuisson

4
你想要设置args的值,例如:

:set args

Prelude> import System.Environment
Prelude System.Environment> getArgs
[]
Prelude System.Environment> :set args ["foo","bar"]
Prelude System.Envrionment> getArgs
["foo","bar"]

关于类型签名问题,这里的 main 函数的类型是由 print v 决定的。在它之前的所有内容都被 >> 运算符忽略。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接