我有一个像下面的测试函数,用于runST内部变异状态.我go在其中定义了另一个函数,它返回Int包含在ST结果中(只是玩一些ST概念).问题是我的函数类型签名似乎是错误的.如果我注释掉函数类型签名,代码运行正常.使用注释代码中的类型签名,它不会编译,因为编译器将go函数的状态解释为与封闭范围中的状态不同.我将理解如何定义函数类型签名以将外部函数传递ST s给go函数的指针.
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Data.Word(Word32)
import Data.Vector.Unboxed as U hiding (mapM_,create)
import Control.Monad.ST as ST
import Control.Monad.Primitive (PrimState)
import System.Random.MWC
test :: Word32 -> Int
test x = runST $ do
gen <- initialize (U.singleton $ fromIntegral x :: U.Vector Word32) :: (forall s. ST s (Gen (PrimState (ST s))))
let --go :: Int -> ST s Int
go x = do
v <- uniformR (1,x) gen
return v
i <- go 100
return i
Run Code Online (Sandbox Code Playgroud)
如果我取消注释类型签名,这是我得到的编译器错误go :: Int -> ST s Int:
Couldn't match type `s1' with `s'
`s1' is a rigid type variable bound by
the type signature for go :: Int -> ST s1 Int at A.hs:12:16
`s' is a rigid type variable bound by
a type expected by the context: ST s Int at A.hs:10:10
Expected type: Gen (PrimState (ST s1))
Actual type: Gen s
In the second argument of `uniformR', namely `gen'
In a stmt of a 'do' block: v <- uniformR (1, x) gen
In the expression:
do { v <- uniformR (1, x) gen;
return v }
Run Code Online (Sandbox Code Playgroud)
麻烦的是,当你说
gen <- ... :: (forall s. ST s (Gen (PrimState (ST s))))
Run Code Online (Sandbox Code Playgroud)
s现在固定为任何s runST提供 - 即我们不能认为它是一个类型变量,因为你的签名会让你相信[1].当编译器说"刚性类型变量"时,这意味着它.为了强调它是固定的,让我们参考它作为S1这个答案.
请注意
let go :: Int -> ST s Int
Run Code Online (Sandbox Code Playgroud)
相当于
let go :: forall s. Int -> ST s Int
Run Code Online (Sandbox Code Playgroud)
即,go必须为任何 工作s.但是你说
v <- uniformR (1,x) gen
Run Code Online (Sandbox Code Playgroud)
它试图绑定类型的计算ST S1 <something>. go应该与任何人合作 s,而不仅仅是S1,所以这是一个错误.正确的签名go是Int -> ST S1 Int,但当然我们只是S1为了论证而弥补,并且true S1在源文件中没有名称,因此go即使它是良好类型的,也不能给出签名.
[1]哦,你已经ScopedTypeVariables开始了,所以看起来就像forall是在那里,因为你正试图扩大范围s.这不起作用 - 范围变量仅适用于函数体forall.您可以通过将签名移动到以下的左侧来解决此问题<-:
(gen :: Gen (PrimState (ST s))) <- initialize ...
Run Code Online (Sandbox Code Playgroud)
之后s将适当确定范围.