模式绑定中的单态限制

Fra*_*ser 2 haskell

{-# LANGUAGE NoMonomorphismRestriction #-}
module Try where

f :: IO (a -> IO String)
f = return $ const getLine

main :: IO ()
main = do
  g <- f
    :: IO (a -> IO String)
  g "String" >>= print
  g 5 >>= print
Run Code Online (Sandbox Code Playgroud)

即使使用NoMonomorphismRestriction标志和显式类型签名,该模块也无法编译Couldn't match expected type ‘[Char]’ with actual type ‘Int’,尽管g它是完全多态的.

luq*_*qui 5

这不是单态限制的含义.单态限制表示如果一个定义没有类型签名并且左侧没有参数,它将专门用于单态类型(或者更确切地说只是单态以消除任何类约束).您已提供类型签名,因此不适用.

这里的问题是你给出了错误的类型f.

f :: IO (a -> IO String)
Run Code Online (Sandbox Code Playgroud)

实际意味着

f :: forall a. IO (a -> IO String)
Run Code Online (Sandbox Code Playgroud)

也就是说,选择一个类型a,那么你就可以绑定到拿到类型的单态函数a -> IO Stringa.这个程序没有问题,例如:

main = do
    g <- f
    g "String" >>= print
    g' <- f
    g' 5 >>= print
Run Code Online (Sandbox Code Playgroud)

但您的用法示例需要以下类型:

f :: IO (forall a. a -> IO String)
Run Code Online (Sandbox Code Playgroud)

也就是说,您希望先绑定并稍后选择类型,即使用多种类型的函数.这被称为"不可预测的类型",不幸的是GHC并没有支持它们很长一段时间,据我所知.

解决此问题的方法是创建一个newtype显式量化内部多态类型的包装器:

newtype R = R { getR :: forall a. a -> IO String }

f :: IO R
f = return $ R (const getLine)

main :: IO ()
main = do
    g <- f
    getR g "String" >>= print
    getR g 5 >>= print
Run Code Online (Sandbox Code Playgroud)