在数据结构中有ST(U)数组?

时间:2013-07-19 08:40:05

标签: arrays haskell typeclass starray

如何让GHC接受此代码:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

module STTest where

import Data.Array.ST
import Control.Monad.ST.Strict as S
import Control.Monad.ST.Lazy as L

-- ST monad arrays (unboxed in actual code)
type Arr s a = STArray s Int a

-- representing some algorithm that works on these STArrays
data ArrGen s a = ArrGen (a -> S.ST s (Arr s a)) (Arr s a -> S.ST s ())

-- class for some "generator"
class Generator g a where
  gen :: g -> a -> [a]

instance Generator (ArrGen s a) a where
  gen (ArrGen create apply) s = L.runST $ do
    a <- strictToLazyST $ create s -- DOES NOT WORK
    strictToLazyST $ apply a >> getElems a

我得到的错误如下:

Couldn't match type `s' with `s1'
  `s' is a rigid type variable bound by
     the instance declaration at STTest.hs:20:28
  `s1' is a rigid type variable bound by
     a type expected by the context: L.ST s1 [a] at STTest.hs:21:33

然而,这很好用:

data Dummy
create' :: a -> S.ST s (Arr s a)
create' = undefined
apply' :: Arr s a -> S.ST s [a]
apply' = undefined

instance Generator Dummy a where
  gen _ s = L.runST $ do
    a <- strictToLazyST $ create' s
    strictToLazyST $ apply' a >> getElems a

为什么它适用于第二个而不是第一个?我可以用数据声明做些什么来使它工作?或者我可以在实例声明中添加某种“forall”吗?

以上只是一个最小的测试程序。我实际上永远循环应用以创建输出值的无限流。 (所以我不能将这两个步骤合并在一起。)我真的希望能够为ArrGen数据类型实例化一次,然后使用这些STArray算法制作它的各种值。

编辑:

没想到把功能放在ArrGen中(我把它放在整体类型上)。虽然现在我遇到了让它在STUArray上工作的问题。就像我使用以下内容一样:

class (Integral a, Bits a, forall s. MArray (STUArray s) a (S.ST s)) => HasSTU a
type AC a = (HasSTU a) => forall s. a -> S.ST s (STUArray s Int a)
type AU a = (HasSTU a) => forall s. STUArray s Int a -> S.ST s ()
type TX a = (HasSTU a) => a -> a -- or without the context
data ArrayGen a = AG (AC a) (AU a) (TX a)

然后失败了:

instance (HasSTU a) => Generator (ArrayGen a) a [a] where
  gens (AG c u p) s = fmap (fmap p) $ L.runST $ do
    ar <- strictToLazyST $ (c s)
    streamM $ strictToLazyST $ u ar >> getElems ar -- can't use getElems here!

streamM :: (Applicative f) => f a -> f (Stream a))
streamM = Cons <$> a <*> streamM a

它抱怨道:

Could not deduce (MArray (STUArray s) a (S.ST s))
  arising from a use of `getElems'
from the context (HasSTU a)

即使上下文(HasSTU a)说(在我看来)所有s都有(MArray(STUArray s)a(S.ST s))上下文,但它似乎并不这么认为。我尝试通过更改(AU a)类型来修复它:

type AU a = (HasSTU a) => forall s. STUArray s Int a -> S.ST s [a]

似乎键入检查,但我无法实际使用它。同样,如果我改为:

class (Integral a, Bits a, forall s. MArray (STUArray s) a (S.ST s)) => HasSTU s a
type AC a = (forall s. HasSTU s a) => a -> S.ST s (STUArray s Int a)
...
instance (forall s. HasSTU s a) => Generator (ArrayGen a) a [a] where
  ...

instance forall s. HasSTU s Word32 -- !!!

但是当我尝试运行时:

Could not deduce (forall s. HasSTU s Word32)

我讨厌这个!为什么?我有一个所有的实例!而且我真的迷失了我应该放在哪里以及真正发生的事情。

1 个答案:

答案 0 :(得分:2)

问题是runST需要forall s. ST s t参数,但您的类型修复s,因此在monadic动作中使用createapply使其不适合runST

在我看来,您的用例禁止提供ArrGen多态(s)个参数,所以

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, RankNTypes #-}

module STTest where

import Data.Array.ST
import Control.Monad.ST.Strict as S
import Control.Monad.ST.Lazy as L

-- ST monad arrays (unboxed in actual code)
type Arr s a = STArray s Int a

-- representing some algorithm that works on these STArrays
data ArrGen a = ArrGen (forall s. a -> S.ST s (Arr s a)) (forall s. Arr s a -> S.ST s ())

-- class for some "generator"
class Generator g a where
  gen :: g -> a -> [a]

instance Generator (ArrGen a) a where
  gen (ArrGen create apply) s = L.runST $ do
    a <- strictToLazyST $ create s -- DOES NOT WORK
    strictToLazyST $ apply a >> getElems a

使组件具有多态性(至少在它编译的意义上,你的用例可能禁止这种方法)。

  

为什么它适用于第二个而不是第一个?

因为那里s没有修复,s中的计算完全是多态的,runST要求。