8971
Haskell の ST モナドを使った破壊的な処理で、処理の一部分を外から渡せるようにしようとしています。  
たとえば下記の `printSum1` では全ての処理をハードコードしています。これを `printSum3`
のように、一部の処理を引数で与えるようにしたいのですが、`printSum3` は `proc` を評価する行で型エラーになってしまいます。  
`printSum2` のように、引数を取っても使わなければエラーにならないようです。  
エラーを見たところでは、`ST s ()` と `ST s1 ()`
という異なる型になっていて、互換性がないということのようですが…なぜそうなるのでしょうか？  
`printSum3` を通す方法はありますか？  
よろしくお願いします。

* * *
```

    import Control.Monad.ST
    import Data.STRef
    
    add10 :: STRef s Int -> ST s ()
    add10 r = modifySTRef r (+ 10)
    
    -- add10 をハードコードする
    printSum1 :: IO ()
    printSum1 =
        print sum
        where
            sum :: Int
            sum = runST $ do
                r <- newSTRef 0
                modifySTRef r (+ 1)
                modifySTRef r (+ 1)
                add10 r -- ★
                readSTRef r
    
    -- 関数を外から受け取るが、使わない
    printSum2 :: (STRef s Int -> ST s ()) -> IO ()
    printSum2 proc =
        print sum
        where
            sum :: Int
            sum = runST $ do
                r <- newSTRef 0
                modifySTRef r (+ 1)
                modifySTRef r (+ 1)
                add10 r -- ★
                readSTRef r
    
    -- 外から受け取った関数を使う……これだけが型エラーになる
    printSum3 :: (STRef s Int -> ST s ()) -> IO ()
    printSum3 proc =
        print sum
        where
            sum :: Int
            sum = runST $ do
                r <- newSTRef 0
                modifySTRef r (+ 1)
                modifySTRef r (+ 1)
                proc r -- ★
                readSTRef r
    
    main = do
        printSum1
        printSum2 add10
        printSum3 add10
    
```

* * *
```

    exper\st.hs:43:25:
        Couldn't match type `s' with `s1'
          `s' is a rigid type variable bound by
              the type signature for
                printSum3 :: (STRef s Int -> ST s ()) -> IO ()
              at exper\st.hs:34:14
          `s1' is a rigid type variable bound by
               a type expected by the context: ST s1 Int at exper\st.hs:39:23
        Expected type: ST s1 ()
          Actual type: ST s ()
        In the return type of a call of `proc'
        In a stmt of a 'do' block: proc r
        In the second argument of `($)', namely
          `do { r <- newSTRef 0;
                modifySTRef r (+ 1);
                modifySTRef r (+ 1);
                proc r;
                .... }'
    
```
CC BY-SA 3.0
2015-04-09T17:39:02.377
8965
2015-04-09T23:28:09.937
9245
question
haskell
Haskell: ST モナドを扱う高階関数での型エラー
`runST`の型は、`runST :: (forall s. ST s a) ->
a`なので、`runST`に渡すアクションは任意の`s`に対して使用できる（`s`に対してポリモーフィックである）必要があります。

ところが、`printSum3`の型が`(STRef s Int -> ST s ()) -> IO
()`なので、`proc`は、`printSum3`全体で固定されたある特定の`s`に対してしか使用できません。このため、あらゆる`s`に対して使用できる必要のある`runST`に渡している部分（`do`ブロックの部分）の中から、特定の`s`に対してしか使えない`proc`を呼び出すことができないのです。

`proc`が任意の`s`に対して使用できることを明示すれば良いので、`RankNTypes`拡張（または、`Runk2Types`拡張）を使用して、型を以下のように指定すれば`printSum3`を書くことができます。

```

    {-# LANGUAGE RankNTypes #-}
    ...
    printSum3 :: (forall s. STRef s Int -> ST s ()) -> IO ()
    
```
CC BY-SA 3.0
2015-04-09T23:28:09.937
8971
2015-04-09T23:28:09.937
3066
8965
answer