Наличие массивов ST(U) в структуре данных?

Что мне нужно сделать, чтобы 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 ответ

Решение

Проблема в том, что runST требует forall s. ST s t аргумент, но ваш тип исправляет sтак что использование create а также apply в монадическом действии делает его непригодным для 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,

Другие вопросы по тегам