我想使用ST
monad和STUArray
来实现算法,我希望它能够同时使用Float
和Double
数据。< / p>
我将演示一个更简单的示例问题:计算一个memoized scanl (+) 0
(我知道它可以在没有STUArray
的情况下解决,仅使用示例)。
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
import Control.Monad
import Control.Monad.ST
import Data.Array.Unboxed
import Data.Array.ST
accumST :: forall a. (IArray UArray a, Num a) => [a] -> Int -> a
accumST vals = (!) . runSTUArray $ do
arr <- newArray (0, length vals) 0 :: ST s (STUArray s Int a)
forM_ (zip vals [1 .. length vals]) $ \(val, i) ->
readArray arr (i - 1)
>>= writeArray arr i . (+ val)
return arr
这失败了:
Could not deduce (MArray (STUArray s) a (ST s)) from the context ()
arising from a use of 'newArray'
Possible fix:
add (MArray (STUArray s) a (ST s)) to the context of
an expression type signature
or add an instance declaration for (MArray (STUArray s) a (ST s))
我无法应用建议的“可能修复”。因为我需要在上下文中添加类似(forall s. MArray (STUArray s) a (ST s))
的内容,但是这是不可能的......
答案 0 :(得分:4)
不幸的是,您当前无法创建一个上下文,该上下文要求未装箱的数组可用于特定类型。不允许量化约束。但是,您仍然可以完成您正在尝试执行的操作(具有特定于类型的代码版本的附加优势。)对于更长的函数,您可以尝试拆分常用表达式,以便重复的代码尽可能小
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
module AccumST where
import Control.Monad
import Control.Monad.ST
import Data.Array.Unboxed
import Data.Array.ST
import Data.Array.IArray
-- General one valid for all instances of Num.
-- accumST :: forall a. (IArray UArray a, Num a) => [a] -> Int -> a
accumST :: forall a. (IArray UArray a, Num a) => [a] -> Int -> a
accumST vals = (!) . runSTArray $ do
arr <- newArray (0, length vals) 0 :: (Num a) => ST s (STArray s Int a)
forM_ (zip vals [1 .. length vals]) $ \(val, i) ->
readArray arr (i - 1)
>>= writeArray arr i . (+ val)
return arr
accumSTFloat vals = (!) . runSTUArray $ do
arr <- newArray (0, length vals) 0 :: ST s (STUArray s Int Float)
forM_ (zip vals [1 .. length vals]) $ \(val, i) ->
readArray arr (i - 1)
>>= writeArray arr i . (+ val)
return arr
accumSTDouble vals = (!) . runSTUArray $ do
arr <- newArray (0, length vals) 0 :: ST s (STUArray s Int Double)
forM_ (zip vals [1 .. length vals]) $ \(val, i) ->
readArray arr (i - 1)
>>= writeArray arr i . (+ val)
return arr
{-# RULES "accumST/Float" accumST = accumSTFloat #-}
{-# RULES "accumST/Double" accumST = accumSTDouble #-}
Generic Unboxed版本(不起作用)将具有如下类型约束:
accumSTU :: forall a. (IArray UArray a, Num a,
forall s. MArray (STUArray s) a (ST s)) => [a] -> Int -> a
您可以简化如下:
-- accumST :: forall a. (IArray UArray a, Num a) => [a] -> Int -> a
accumST :: forall a. (IArray UArray a, Num a) => [a] -> Int -> a
accumST vals = (!) . runSTArray $ do
arr <- newArray (0, length vals) 0 :: (Num a) => ST s (STArray s Int a)
accumST_inner vals arr
accumST_inner vals arr = do
forM_ (zip vals [1 .. length vals]) $ \(val, i) ->
readArray arr (i - 1)
>>= writeArray arr i . (+ val)
return arr
accumSTFloat vals = (!) . runSTUArray $ do
arr <- newArray (0, length vals) 0 :: ST s (STUArray s Int Float)
accumST_inner vals arr
accumSTDouble vals = (!) . runSTUArray $ do
arr <- newArray (0, length vals) 0 :: ST s (STUArray s Int Double)
accumST_inner vals arr
{-# RULES "accumST/Float" accumST = accumSTFloat #-}
{-# RULES "accumST/Double" accumST = accumSTDouble #-}
答案 1 :(得分:4)
所以这就是我现在要解决的问题 - 为(forall s. MArray (STUArray s) a (ST s))
创建一个新的类型类型:
class IArray UArray a => Unboxed a where
newSTUArray :: Ix i => (i, i) -> a -> ST s (STUArray s i a)
readSTUArray :: Ix i => STUArray s i a -> i -> ST s a
writeSTUArray :: Ix i => STUArray s i a -> i -> a -> ST s ()
instance Unboxed Float where
newSTUArray = newArray
readSTUArray = readArray
writeSTUArray = writeArray
instance Unboxed Double where
newSTUArray = newArray
readSTUArray = readArray
writeSTUArray = writeArray
虽然我对此并不十分满意,但我更喜欢它,因为: