Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining PrimMonad instance for STT? (ST Transformer)

Data.Vector.Mutable seems to require an instance of PrimMonad in ST s and IO monads.

The typeclass is defined as this --

-- | Class of primitive state-transformer monads
class Monad m => PrimMonad m where
  -- | State token type
  type PrimState m

  -- | Execute a primitive operation
  primitive :: (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a

  -- | Expose the internal structure of the monad
  internal :: m a -> State# (PrimState m) -> (# State# (PrimState m), a #)

They're implemented like this --

instance PrimMonad IO where
  type PrimState IO = RealWorld
  primitive = IO
  internal (IO p) = p

instance PrimMonad (ST s) where
  type PrimState (ST s) = s
  primitive = ST
  internal (ST p) = p

I don't really understand at all what any of the functions of the typeclass are supposed to do, or how the implementations work.

But I need to implement it for STT (the one given by http://hackage.haskell.org/package/STMonadTrans-0.3.1)

STT has constructor STT s m a

In my naive attempt I tried replacing everything ST s with STT s m:

instance Monad m => PrimMonad (STT s m) where
  type PrimState (STT s m) = s
  primitive = STT
  internal (STT p m) = p

but I get this error:

Not in scope: data constructor `STT'

for the definitions of primitive and internal, despite having used STT multiple times throughout the program already (although I guess as a type constructor?).

How should I properly implement this typeclass?

(I will eventually be using this as STT s (Rand g) a)


EDIT: I imported Control.Monad.ST.Trans.Internal to get STT as a data constructor, and these are the new errors: (after changing internal (STT s m) to internal (STT s))

Couldn't match kind `*' against `ArgKind'
Kind incompatibility when matching types:
  m0 :: * -> *
  (#,#) (ghc-prim:GHC.Prim.State# (PrimState (STT s m))) :: ArgKind
                                                            -> (#)
In the expression: STT
In an equation for `primitive': primitive = STT


Couldn't match type `m'
               with `(#,#) (ghc-prim:GHC.Prim.State# (PrimState (STT s m)))'
  `m' is a rigid type variable bound by
      the instance declaration at src/pimc/PIMC.hs:41:16
Expected type: ghc-prim:GHC.Prim.State# (PrimState (STT s m))
               -> (# ghc-prim:GHC.Prim.State# (PrimState (STT s m)), a #)
  Actual type: ghc-prim:GHC.Prim.State# s -> m (STTRet s a)
In the expression: p
In an equation for `internal': internal (STT p) = p



Couldn't match type `a' with `STTRet s a'
  `a' is a rigid type variable bound by
      the type signature for
        internal :: STT s m a
                    -> ghc-prim:GHC.Prim.State# (PrimState (STT s m))
                    -> (# ghc-prim:GHC.Prim.State# (PrimState (STT s m)), a #)
      at src/pimc/PIMC.hs:44:3
Expected type: ghc-prim:GHC.Prim.State# (PrimState (STT s m))
               -> (# ghc-prim:GHC.Prim.State# (PrimState (STT s m)), a #)
  Actual type: ghc-prim:GHC.Prim.State# s -> m (STTRet s a)
In the expression: p
In an equation for `internal': internal (STT p) = p
like image 749
Justin L. Avatar asked Jul 08 '13 08:07

Justin L.


1 Answers

Principally, you might be able to write an implementation for primitive, but not for internal, since it imposes a certain structure for your monad, which is only satisfied by the internal implementations of the IO and ST monads.

However, my impression is that the problem is with the Data.Vector.Mutable module, whose requirements are overly strict. To use for example an IO Vector in a monad m, you principally only need an embedding of IO into m (i.e. the primitive method) and not vice versa (i.e. the internal method). If this is correct, they should try to subdivide the PrimMonad class into an embedding part and an isomorphism part, for example as follows:

-- | Class of primitive state-transformer monads
class Monad m => PrimMonadEmbed m where
  -- | State token type
  type PrimState m

  -- | Execute a primitive operation
  primitive :: (State# (PrimState m) -> (# State# (PrimState m), a #)) -> m a

class PrimMonadEmbed m => PrimMonad m where
  -- | Expose the internal structure of the monad
  internal :: m a -> State# (PrimState m) -> (# State# (PrimState m), a #)

This might be okay in terms of backwards compatibility since no custom instances can exist in user code. Making the requirement less strict would also make their code usable with transformed versions of the IO monad like StateT Int IO etc.

You might try two things:

  • implement a partial instance for PrimMonad which only implements the primitive method and makes internal throw an error, and see if you can use the vector library like that
  • contact the authors of the vector library you are using and ask them if the above proposal is realistic...

By the way, the following implementation of primitive should work (compare the source of STT to understand what went wrong in your code):

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UnboxedTuples #-}

module Test where

import Control.Monad.Primitive
import Control.Monad.ST.Trans
import Control.Monad.ST.Trans.Internal

instance Monad m => PrimMonad (STT s m) where
  type PrimState (STT s m) = s
  primitive f = STT (\s -> case (f s) of (# s', v #) -> return (STTRet s' v))
  internal _ = error "no implementation of internal for STT"
like image 168
Dominique Devriese Avatar answered Nov 07 '22 20:11

Dominique Devriese