问题描述:

The MArray class provides generic functions for working with mutable arrays of various sorts in both ST and IO contexts. I haven't been able to find a similar class for working with both STRefs and IORefs. Does such a thing exist?

The `ref-fd`

package provides it:

```
class Monad m => MonadRef r m | m -> r where
[...]
```

or with type families, `ref-tf`

:

```
class Monad m => MonadRef m where
type Ref m :: * -> *
[...]
```

Another answer suggested the monad-statevar package which doesn't have the functional dependency. It also has separate `HasGet`

and `HasPut`

members and no abstraction over the `newRef`

functionality.

Aside from the different methods in each, the functional dependency is a design trade-off. Consider the following two simplified classes:

```
class MRef1 r m where
newRef1 :: a -> m (r a)
readRef1 :: r a -> m a
class MRef2 r m | m -> r where
newRef2 :: a -> m (r a)
readRef2 :: r a -> m a
```

With `MRef1`

, both the monad type and the reference type can vary freely, so the following code has a type error:

```
useMRef1 :: ST s Int
useMRef1 = do
r <- newRef1 5
readRef1 r
No instance for (MRef1 r0 (ST s)) arising from a use of `newRef1'
The type variable `r0' is ambiguous
```

We have to add an extra type signature somewhere to say that we want to use `STRef`

.

In contrast, the same code works fine for `MRef2`

without any extra signature. The signature on the definition saying that the whole code has type `ST s Int`

, combined with the functional dependency `m -> r`

means that there is only one `r`

type for a given `m`

type and so the compiler knows that our existing instance is the only possible one and we must want to use `STRef`

.

On the flip side, suppose we want to make a new kind of reference, e.g. `STRefHistory`

that tracks all the values that have ever been stored in it:

```
newtype STRefHistory s a = STRefHistory (STRef s [a])
```

The `MRef1`

instance is fine because we are allowed multiple reference types for the same monad type:

```
instance MRef1 (STRefHistory s) (ST s) where
newRef1 a = STRefHistory <$> newSTRef [a]
readRef1 (STRefHistory r) = head <$> readSTRef r
```

but the equivalent `MRef2`

instance fails with:

```
Functional dependencies conflict between instance declarations:
instance MRef2 (STRef s) (ST s) -- Defined at mref.hs:28:10
instance MRef2 (STRefHistory s) (ST s) -- Defined at mref.hs:43:10
```

I also mentioned the type family version, which is quite similar in expressive power to the functional dependency; the reference type is a "type function" of the monad type so there can again only be one per monad. The syntax ends up being a bit different and in particular you can just say `MonadRef m`

in constraints, without stating what the reference type is within the constraint.

It's also plausible to have the reversed functional dependency:

```
class MRef2 r m | r -> m where
```

so that each reference type can live in just one monad, but you can still have several reference types for a monad. Then you'd need type signatures on your references but not on your monadic computations as a whole.

Control.Monad.StateVar has a typeclass which lets you `get`

and `put`

IORefs and STRefs identically.