tôi sẽ cố gắng giải thích lý do cho runST
's loại:
runST :: (forall s. ST s a) -> a
Và tại sao nó không giống như này đơn giản:
alternativeRunST :: ST s a -> a
Lưu ý rằng điều này sẽ alternativeRunST
đã từng làm việc cho chương trình của bạn.
alternativeRunST
cũng đã cho phép chúng tôi bị rò rỉ biến ra khỏi ST
đơn nguyên:
leakyVar :: STRef s Int
leakyVar = alternativeRunST (newSTRef 0)
evilFunction :: Int -> Int
evilFunction x =
alternativeRunST $ do
val <- readSTRef leakyVar
writeSTRef leakyVar (val+1)
return (val + x)
Sau đó, bạn có thể đi trong ghci và làm:
>>> map evilFunction [7,7,7]
[7,8,9]
evilFunction
không referentially minh bạch!
Btw, để thử nó ra cho mình đây là "xấu ST" khuôn khổ cần thiết để chạy đoạn code trên:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad
import Data.IORef
import System.IO.Unsafe
newtype ST s a = ST { unST :: IO a } deriving Monad
newtype STRef s a = STRef { unSTRef :: IORef a }
alternativeRunST :: ST s a -> a
alternativeRunST = unsafePerformIO . unST
newSTRef :: a -> ST s (STRef s a)
newSTRef = ST . liftM STRef . newIORef
readSTRef :: STRef s a -> ST s a
readSTRef = ST . readIORef . unSTRef
writeSTRef :: STRef s a -> a -> ST s()
writeSTRef ref = ST . writeIORef (unSTRef ref)
Các thực runST
không cho phép chúng tôi xây dựng như "ma quỷ" chức năng. Nó làm như thế nào? Nó kinda khó khăn, xem dưới đây:
Đang cố gắng để chạy:
>>> runST (newSTRef "Hi")
error:
Couldn't match type `a' with `STRef s [Char]'
...
>>> :t runST
runST :: (forall s. ST s a) -> a
>>> :t newSTRef "Hi"
newSTRef "Hi" :: ST s (STRef s [Char])
newSTRef "Hi"
không phù hợp (forall s. ST s a)
. Như cũng có thể được nhìn thấy sử dụng một ví dụ thậm chí đơn giản hơn, nơi GHC cho chúng ta một lỗi khá đẹp:
dontEvenRunST :: (forall s. ST s a) -> Int
dontEvenRunST = const 0
>>> dontEvenRunST (newSTRef "Hi")
<interactive>:14:1:
Couldn't match type `a0' with `STRef s [Char]'
because type variable `s' would escape its scope
Lưu ý rằng chúng ta cũng có thể viết
dontEvenRunST :: forall a. (forall s. ST s a) -> Int
Và nó tương đương với bỏ qua forall a.
như chúng ta đã làm trước đó.
Lưu ý rằng phạm vi của a
lớn hơn so với s
, nhưng trong trường hợp của newSTRef "Hi"
giá trị của nó nên phụ thuộc vào s
. Hệ thống kiểu không cho phép điều này.
Chà, điều đó thật tuyệt vời! Cảm ơn vì lời giải thích chi tiết. –