能够从Haskell中的monadic动作获得多态函数

Kos*_*kov 3 haskell

我希望能够编写一个IO动作,它将返回一个多态函数,可以用于不同的值,但似乎我不能.有人可以帮我这样做吗?

f :: a -> Bool
f _ = True

getF :: Int -> (a -> Bool)
getF _ = f

getFIO :: IO (a -> Bool)
getFIO = return f

main :: IO ()
main = do
    -- this works
    print ((f :: Int -> Bool) (10::Int))
    print ((f :: String -> Bool) "asd")

    -- this works
    let f' = getF 10
    print (f' (10::Int))
    print (f' "asd")

    -- this doesn't
    f'' <- getFIO
    print (f'' (10::Int))
    print (f'' "asd")

    -- but this does
    f''' <- getFIO
    print (f''' (10::Int))
    f'''' <- getFIO
    print (f'''' "asd")

    return ()
Run Code Online (Sandbox Code Playgroud)

dfe*_*uer 8

Haskell不允许您对类型构造函数使用多态参数->.您需要使用a newtype来使用该特殊豁免.

{-# LANGUAGE RankNTypes #-}
newtype ToBool = ToBool {getToBool :: forall a . a -> Bool}

f :: ToBool
f = ToBool (const True)

getFIO :: IO ToBool
getFIO = return f
Run Code Online (Sandbox Code Playgroud)

getToBool每次你想要使用时都需要申请f,但没关系.


chi*_*chi 5

为了补充其他答案,我会注意到

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

实际意味着

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

它描述了调用者和被调用者之间的以下契约:

  1. 呼叫者选择一种类型 a
  2. 然后,f做一些IO
  3. 最后,f返回一个类型的值a -> Bool

注意,a在IO之前选择,因此最后一个函数是单态的.

相反,我们需要以下类型

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

这推迟了aIO完成后的选择.上面的类型需要ImpredicativeTypes扩展(在GHC中当前不能正常工作),或者使用newtype其他答案中的包装器.