如何将[Either ST]
转换为Either [ST]
然后按顺序执行操作?以下代码似乎适用于运行ST操作列表,但是当尝试在Either中生成操作列表(交换下面的test
定义)时,类型不再排列。我本来希望列表的类型是相同的,所以我非常感谢对这些差异的任何解释。
{-# LANGUAGE RankNTypes #-}
import qualified Data.STRef as ST
import qualified Control.Monad.ST as ST
run :: (forall s. [ST.STRef s Int -> ST.ST s Int]) -> Int
run fs =
ST.runST $ do
x <- ST.newSTRef 0
mapM_ (\f ->
f x >>= ST.writeSTRef x
) fs
ST.readSTRef x
action :: ST.STRef s Int -> ST.ST s Int
action = \x -> ST.readSTRef x >>= \y -> return (y + 1)
-- test :: Either String Int
-- test = mapM (const (return action)) [0..5] >>= \fs -> return (run fs)
test :: Int
test = run (map (const action) [0..5])
main = print test
答案 0 :(得分:10)
存在更高级别类型时的类型推断是不可判定的。 GHC的类型推理算法做了一些简化的假设,这使得它不完整。其中包括:
GHC将假设由lambda绑定的变量是一个单一型(它不包含(领先?)forall
s)
当您使用多态函数时,GHC将假设其类型中的自由类型变量将在monotypes实例化
如果您礼貌地要求GHC使用特定的多种类型,则可以覆盖这两种假设。
现在,您希望您的程序如何键入支票?
run fs
键入支票,我们最好有fs :: forall s. [ST.STRef s Int -> ST.ST s Int]
\(fs :: forall s. [ST.STRef s Int -> ST.ST s Int]) -> ...
(使用ScopedTypeVariables
)现在考虑使用>>=
。它的类型是Monad m => m a -> (a -> m b) -> m b
。但是,我们需要a = forall s. [ST.STRef s Int -> ST.ST s Int]
。所以,根据上面的第二点,我们需要给这个>>=
一个类型签名,如
... `op` (\(fs :: forall s. [ST.STRef s Int -> ST.ST s Int]) -> ...)
where op :: Monad m
=> m (forall s. [ST.STRef s Int -> ST.ST s Int])
-> ((forall s. [ST.STRef s Int -> ST.ST s Int]) -> m b)
-> m b
op = (>>=)
op
的此应用程序中,第一个参数的类型为Either String (forall s. [ST.STRef s Int -> ST.ST s Int])
。除非您打开(损坏的)ImpredicativeTypes,否则不允许将类型构造函数(除(->)
之外)应用于多边形类型。但是,我们可以打开并继续...... return :: Monad m => a -> m a
实例化a = forall s. ST.STRef s Int -> ST.ST s Int
。因此,我们需要为return
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
b = forall s. ST.STRef s Int -> ST.ST s Int
如果您密切关注,您会发现另一个问题:mapM
的结果有类型
Either String [forall s. ST.STRef s Int -> ST.ST s Int]
但(>>=)
的参数必须是
Either String (forall s. [ST.STRef s Int -> ST.ST s Int])
你需要在这些之间进行转换。实际上这是一个无操作,但GHC并不够聪明,所以你必须做一个线性时间转换,比如
liftM (\x -> map (\(y :: forall s. ST.STRef s Int -> ST.ST s Int) -> y) x)
(liftM
除外需要另一种类型签名)
故事的道德:你可以做到这一点,但你不应该这样做。
如果你将forall
隐藏在像
newtype S s = S { unS :: forall s. ST.STRef s Int -> ST.ST s Int }
这使得在您的程序中更明确地引入了多态性的点(通过出现S
和unS
)。
答案 1 :(得分:6)
您需要将函数包装在newtype
中以保留存在量化,而不必使用ImpredicativeTypes
。
{-# LANGUAGE RankNTypes #-}
import qualified Data.STRef as ST
import qualified Control.Monad.ST as ST
newtype STFunc = STFunc (forall s. ST.STRef s Int -> ST.ST s Int)
run :: [STFunc] -> Int
run fs = ST.runST $ do
x <- ST.newSTRef 0
mapM_ (\(STFunc f) ->
f x >>= ST.writeSTRef x
) fs
ST.readSTRef x
action :: STFunc
action = STFunc $ \x -> ST.readSTRef x >>= \y -> return (y + 1)
test :: Either String Int
test = mapM (const (return action)) [0..5] >>= \fs -> return (run fs)
main = print test