MArray specialization to STArray

484 views Asked by At

I am confused about using the STArray in Haskell with polymorphism.

Suppose I have the following setup

data SomeData a = Something a

thawData :: MArray u a m => SomeData a -> m (u Int a)
thawData = {- doesn't matter -}

doSomething :: SomeData a -> ()
doSomething x = runST $ do 
  xArr <- thawData x
  return ()

now, I believe the type of thawData specializes in this case to

thawData :: SomeData a -> ST s (STArray Int a)

However, this does not compile unless I change the type of thawData to explicitly use the STArray, even if I try to explicitly type it in the body of the do expression.

So what on earth is going on? Why can't the type specialize?
Can I somehow change the body of doSomething rather than the type of thawData?

Thanks!

2

There are 2 answers

2
Daniel Fischer On BEST ANSWER

now, I believe the type of thawData specializes in this case to

thawData :: SomeData a -> ST s (STArray Int a)

Not quite, STArrays are parametrized by the state type, so it would be ST s (STArray s Int a). But that cannot be inferred, as the error message tells you:

Thaw.hs:13:11:
    No instance for (MArray u0 a (ST s))
      arising from a use of `thawData'
    The type variable `u0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance MArray (GHC.Arr.STArray s) e (ST s)
        -- Defined in `Data.Array.Base'
      instance MArray (Data.Array.Base.STUArray s) Bool (ST s)
        -- Defined in `Data.Array.Base'
      instance MArray (Data.Array.Base.STUArray s) Char (ST s)
        -- Defined in `Data.Array.Base'
      ...plus 15 others
    Possible fix: add an instance declaration for (MArray u0 a (ST s))
    In a stmt of a 'do' block: xArr <- thawData x

there are several possible instances of the MArray class with ST s as the monad to choose from (and even if only one instance was in scope, the compiler operates on an open world assumption, other instances could be defined somewhere else). So the compiler doesn't know which instance to use, hence it refuses to compile the code.

Now, the possible fix suggested isn't the right one here, what you need is to fix the array type by another means. One possibility is to specialise the type signature of thawData at the top level, as you did.

Another is to specialise it in doSomething instead,

doSomething :: SomeData a -> ()
doSomething x = runST $ do 
  xArr <- (thawData :: SomeData b -> ST s (STArray s Int b)) x
  return ()

which tells the compiler at which type thawData is to be used here, and the third is to move the expression type signature to the end of that line

{-# LANGUAGE ScopedTypeVariables #-}
-- imports etc.

doSomething :: forall a. SomeData a -> ()
doSomething x = runST $ do 
  xArr <- thawData x :: ST s (STArray s Int a)
  return ()

but that requires the ScopedTypeVariables extension since now you need to refer to the type that instantiates the type variable a from the signature of doSomething. I find it more readable than the expression type signature in the middle of the line, though.

0
Carl On

If you included the error message, it'd help.

As it is, all I can do is guess that the error is because you don't provide enough context to determine the type of xArr. In particular, m can be unified with ST s and a comes from the argument type, but there's no context for determining u.

My guess is that's the error the compiler is telling you about, but since you didn't include it, that's just a guess.

If I'm right, you can solve this without modifying thawData by putting a type annotation on xArr or the expression that creates it, in such a way as to specify what type u should be.