为什么这不会抛出任何类型的错误?

时间:2010-07-10 17:31:59

标签: haskell type-inference

我正在尝试从其中一个博客中获取一些代码段,然后我注意到以下代码

f :: Int -> [Int]
f x = [1+x,2*x]

test :: IO ()
test = putStrLn . show $ return 5 >>= f >>= f

执行时我得到了[7,12,11,20]。为什么第二个'f'函数调用不是抛出类型错误?它与List Monad有关吗?

2 个答案:

答案 0 :(得分:6)

这正是因为列表是monad。

return 5 >>= f >>= f

在列表monad中(>>=) = flip concatMap,这与

相同
concatMap f $ concatMap f $ return 5

同样在列表monad return x = [x]中,我们有

concatMap f $ concatMap f [5]

现在,concatMap g x = concat (map g x),我们可以将其扩展为

concat $ map f $ concat $ map f [5]

并评估它

concat $ map f $ concat $ [[6, 10]]
concat $ map f $ [6, 10]
concat $ [[7, 12], [11, 20]]
[7, 12, 11, 20]

这有意义吗?

答案 1 :(得分:4)

让我们问一下GHC对某些子表达式的类型是什么。

> :t putStrLn . show
putStrLn . show :: (Show a) => a -> IO ()

这肯定和预期的一样。

> :t return 5 >>= f >>= f
return 5 >>= f >>= f :: [Int]

我想我们已经预料到了,但它没有回答你的问题。接下来,快速提醒该表达式如何解析:

> :i (>>=)
class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  ...
infixl 1 >>=

好吧,它是关联的,所以表达式是(return 5 >>= f) >>= f

> :t return 5 >>= f
return 5 >>= f :: [Int]

将此与(>>=)的上述类型签名相比较,我们可以观察到两件事:

  • (>>=)的第二个参数必须返回某种monad
  • (>>=)的第一个参数必须是同一个monad中的值

因此,我们知道f必须在此处显示类似(a -> m b)的类型,f的实际类型签名为Int -> [Int]。所以我们可以手动组合这些类型:

  • a = Int
  • b = Int
  • m = []

请注意,([] a)[a]的含义相同。

所以monad确实是monad的名单。那是怎么回事?

> return 5 :: ([] Int)
[5]
> [5] >>= f
[6, 10]
> ([5] >>= f) >>= f
[7, 12, 11, 20]