考虑以下IO
代码:
ghci> let x = return 100 :: IO Int
ghci> :t do { a <- x; print a; return 500 }
do { a <- x; print a; return 500 } :: Num b => IO b
我对do notation / bind的理解是编译器将强制执行以下签名:
ghci> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
在上面的例子中,我的理解是:
x
的类型为IO Int
print a
的类型为IO ()
return 500
的类型为Num b => IO b
在我看来,IO ()
不符合类型Num b => IO b
。此外,正如我所理解的,IO Int
不符合Num b => IO b
。
如果这个观察是有效的,那么为什么这个代码会编译?每个行(即>>=
)是否必须符合m b
,其中m
等于IO而b
等于Num b => b
?
答案 0 :(得分:3)
您发布的代码涉及以下内容。
x >>= (\a ->
print a >>
return 500)
或者,扩展(>>)
x >>= (\a ->
print a >>= (\_ ->
return 500))
然后,您可以看到,在(>>=)
的不同来电中,a
和b
类型不一定相同。说(>>=) :: Monad m => m a -> (a -> m b) -> m b
。
:x
的类型为IO Int
,\a -> print a >>= (\_ -> return 500)
的类型为Num c => Int -> IO c
,因此我的类型签名中的a
为{ {1}}为(>>=)
,Int
为b
(c
限制)。
在第二个电话中:Num
的类型为print a
,而IO ()
的类型为\_ -> return 500
(Num c => () -> IO c
部分是通过尝试推断的匹配()
的签名,因此(>>=)
的类型签名中的a
为(>>=)
,而()
为b
(仍为c
限制。
答案 1 :(得分:1)
do { a <- x; print a; return 500 }
相当于(return 100 :: Int) >>= print >> return 500
。
>>
的签名为(>>) :: Monad m => m a -> m b -> m b
,与所见内容同步。