2

I am having some trouble dealing with the forall quantifier in ST's type. The following (simplified) code sample works fine, producing the expected result.

import Control.Monad
import Control.Monad.ST

return' :: a -> ST s a
return' = return

function :: a -> a
function x = runST $ do
               -- Some complicated expression eventually producing a result
               return' x

This is fine, if all I want to do is have ST. But in my computation, I want a state transformer computation that might also fail, so I attempt to add ExceptT to the stack.

import Control.Monad
import Control.Monad.ST
import Control.Monad.Except

return' :: a -> ExceptT String (ST s) a
return' = return

function :: a -> Either String a
function x = runST . runExceptT $ do
               -- Some complicated expression that might fail
               return' x

Unfortunately, I get a rather bizarre error message.

example.hs:9:14:
    Couldn't match type `ST s0 (Either String a)'
                   with `forall s. ST s (Either String a)'
    Expected type: ST s0 (Either String a) -> Either String a
      Actual type: (forall s. ST s (Either String a))
                   -> Either String a
    Relevant bindings include
      x :: a (bound at example.hs:9:10)
      function :: a -> Either String a (bound at example.hs:9:1)
    In the first argument of `(.)', namely `runST'
    In the expression: runST . runExceptT

I have only a vague understanding of the rank-2 type used by ST, so I'm not really sure how to approach this error. How can I safely put ST at the bottom of a monad transformer stack without getting problems with the forall?

(For those curious, I'm using ST because I want my computation to use a mutable array and then freeze it at the end. That shouldn't be relevant to this problem, but this is why I can't simply use State instead)

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
  • 2
    `ST` uses RankN types, and type inference with those does not really work well. For instance `runST action` could be OK while `runST $ action` errors out. GHC devs have added a _hack_ in ithe inference engine to treat the latter as the former, but this only applies to `$`. If you use `.` or even a redefinition of `$`, things stop working. – chi Sep 18 '15 at 15:23

1 Answers1

4

All you need to do is replace the . behind runST by $, and then you can use ordinary return instead of return' as well.

import Control.Monad
import Control.Monad.ST
import Control.Monad.Except

function :: a -> Either String a
function x = runST $ runExceptT $ do
               return x

main = return ()

(At least for me this compiles, without problems.)

See this answer for the theory behind the error.

Community
  • 1
  • 1
Sam van Herwaarden
  • 2,321
  • 14
  • 27