我希望能够编写一个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 ()
答案 0 :(得分:8)
Haskell不允许您对->
以外的类型构造函数使用多态参数。您需要使用newtype
来使用该特殊豁免。
{-# LANGUAGE RankNTypes #-}
newtype ToBool = ToBool {getToBool :: forall a . a -> Bool}
f :: ToBool
f = ToBool (const True)
getFIO :: IO ToBool
getFIO = return f
每次要使用getToBool
时,您都需要申请f
,但这没关系。
答案 1 :(得分:5)
为了补充其他答案,我注意到了
f :: IO (a -> Bool)
实际上意味着
f :: forall a. IO (a -> Bool)
描述了来电者和被叫者之间的以下合同:
a
f
执行了一些IO
f
返回a -> Bool
请注意,在IO之前选择a
,因此最后一个函数是单态的。
相反,我们需要以下类型
f :: IO (forall a. a -> Bool)
在IO完成后推迟了a
的选择。上述类型需要ImpredicativeTypes
扩展名(目前在GHC中不起作用),或者使用newtype
包装器,如其他答案中所做的那样。
答案 2 :(得分:4)
你可以使用RankNTypes
“工作”,但是你需要一个newtype包装器:
-- might as well be Bool
newtype Pointless = Pointless (forall a. a -> Bool)
getFIO_ :: IO Pointless
getFIO_ = return (Pointless f)
main = do
Pointless f'' <- getFIO_
print (f'' (10::Int))
print (f'' "asd")
因此,您可能最好重新安排程序,以便不使用该扩展程序,更改getFIO :: IO Blah
,定义applyBlah :: Blah -> a -> Bool
,然后使用之前applyBlah f''
f''
}。