我有以下作業正常的簡化程式:
{-# LANGUAGE Rank2Types #-}
module Temp where
import Control.Monad.ST
import Control.Monad
import Data.STRef
mystery :: Int -> Int
mystery start =
let
run :: ST s Int
run = do
count <- newSTRef (1::Int)
loop count start
readSTRef count
in
runST run
loop :: STRef s Int -> Int -> ST s ()
loop cnt n = when (n /= 1)
(modifySTRef cnt succ >>
if n `mod` 2 == 0
then loop cnt (n `div` 2)
else loop cnt (n * 3 1))
我移動塊loop內的定義do,以便能夠run像這樣使用創建的計數器:
mystery :: Int -> Int
mystery start =
let
run :: ST s Int
run = do
count <- newSTRef (1::Int)
let
loop :: Int -> ST s ()
loop n = when (n /= 1)
(modifySTRef count succ >>
if n `mod` 2 == 0
then loop (n `div` 2)
else loop (n * 3 1))
loop start
readSTRef count
in
runST run
這給了我以下錯誤:
Couldn't match type ‘s1’ with ‘s’
‘s1’ is a rigid type variable bound by
the type signature for:
loop :: forall s1. Int -> ST s1 ()
at ...
‘s’ is a rigid type variable bound by
the type signature for:
run :: forall s. ST s Int
at ...
Expected type: ST s1 ()
Actual type: ST s ()
In the expression:
...
我知道這s是不允許逃脫的,但據我所知它不會?此外,當我洗掉問題的型別簽名loop時。我猜這表明他們的簽名不知何故不正確,但它和以前一樣,除了沒有計數器,我不知道它應該是什么。
重命名s以匹配或不匹配中s提到的run沒有區別。
uj5u.com熱心網友回復:
首先,讓我們重命名型別變數,以便它們更容易討論,并洗掉與此錯誤無關的程式部分:
mystery :: Int
mystery = runST run
where run :: ST s Int
run = do
ref <- newSTRef 1
let read :: ST t Int
read = readSTRef ref
read
這表現出相同的行為,并read像以前一樣注釋掉型別簽名以修復它。
現在,讓我們問:什么是型別ref?newSTRefis的型別a -> ST s (STRef s a),所以ref :: STRef s Int, wheres與sin相同run。
什么是型別readSTRef ref?嗯,readSTRef :: STRef s a -> ST s a。所以,readSTRef ref :: ST s Int,其中 s 又是 定義中的那個run。你給它一個型別簽名,聲稱它適用于 any t,但它只適用于特定的sin run,因為它使用來自該交易的 ref 。
read在loop不打開語言擴展以允許您參考s已經在范圍內的型別變數的情況下,不可能為我的或您的型別撰寫型別。使用ScopedTypeVariables,您可以撰寫:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.ST
import Data.STRef
mystery :: Int
mystery = runST run
where run :: forall s. ST s Int
run = do
ref <- newSTRef 1
let read :: ST s Int
read = readSTRef ref
read
使用forall帶來s成范圍明確,讓你可以參考它。現在內部型別簽名s實際上是指外部型別簽名,而不是一個新的、隱藏的型別變數。這就是你向型別系統承諾你只會read在擁有它使用的 ref 的事務內部使用這個函式的方式。
您的原始程式,具有頂級loop,出于類似的原因作業。它不是捕獲一個STRef(因此是s隱式的),而是宣告了一個型別,該型別s對 ref 和事務都使用相同的型別。它適用于任何交易,前提是它從該交易中獲得了一個參考。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/387022.html
