Haskell多态数组操作

时间:2013-03-31 09:25:44

标签: arrays haskell polymorphism

我正在编写一堆不同的排序,并为列表和数组执行此操作。困扰我的一件事是我可以为像

这样的列表编写多态排序函数
bubblesort :: (Ord a) => [a] -> [a]

但是当我尝试为UArray s做同样的事情时:

alterUArray :: (Ix i, Ord e) => 
               (STUArray s i e -> ST s ()) -> UArray i e -> UArray i e
alterUArray alter ua = runST $ do
    mua <- thaw ua :: ST s1 (STUArray s1 i e)
    alter mua
    freeze mua

来自GHC的long error messages失败(UArray Int Int的版本效果很好)。我尝试指定{-# LANGUAGE ScopedTypeVariables #-},但这并未消除i e调用中thaw类型的歧义。没有thaw类型的错误消息:http://hpaste.org/84910

编写多态UArray操作需要什么?是否有一些基本限制?是否有允许执行此类操作的编译器扩展?

3 个答案:

答案 0 :(得分:3)

有两个问题。首先,作为dave4420 pointed outrunST需要alter函数在状态s中具有多态性。

但是,修复此问题使得无法解决第二个问题,即MArray(和thaw)需要freeze个实例。你需要一个约束

alterUArray :: (Ix i, Ord e, IArray UArray e, forall s. MArray (STUArray s) e (ST s)) => ...

要让它发挥作用,因为runST是选择s的人。但是你不能指定这样的约束。

如果您提供特定的元素类型(IntDouble,...),则可以使用

instance MArray (STUArray s) Int (ST s) where ...

所以thawfreeze的要求无论s选择了runST都得到满足(并且不需要说明约束)。

如果您选择盒装数组而不是未装箱数组,它也有效,因为还有

instance MArray (STArray s) e (ST s) where ...

因此对alterUArray签名中需要声明的元素类型没有约束。 (列表元素的类型没有限制,列表元素被装箱,因此盒装数组是列表的对应关系,而不是未装箱的数组。)

如果您可以忍不住弄脏,可以通过ST s替换IO来解决问题,

alterUArray :: (Ix i, Ord e, MArray IOUArray e IO, IArray UArray e) =>
               (IOUArray i e -> IO ()) -> UArray i e -> UArray i e
alterUArray alter ua = unsafePerformIO $ do
    mua <- thaw ua
    alter mua
    freeze mua

只需要FlexibleContexts。这允许传递一个错误的alter参数,该参数执行邪恶的IO内容,并将其隐藏在调用者之外。因此,让我们通过在unsafePerformIO参数上强制使用更通用的类型来安全使用alter

{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables #-}

import Data.Array.Unboxed
import Data.Array.IO
import System.IO.Unsafe

alterUArray :: forall i e. (Ix i, Ord e, IArray UArray e, MArray IOUArray e IO) =>
               (forall m u. MArray u e m => u i e -> m ()) -> UArray i e -> UArray i e
alterUArray alter ua = unsafePerformIO $ do
    mua <- thaw ua :: IO (IOUArray i e)
    alter mua
    freeze mua

现在我们已经为alter提供了一种类型,使得IO无法使用unsafePerformIO而无法使用unsafePerformIO,因此thaw在这里的使用不会引入额外的不安全感 - 以牺牲更多需要的扩展为代价。

(注意:虽然使用unsafeFreeze获取原始数组的副本是必要的,但在冻结时不需要额外的副本,可能{{1}}没有问题。)

答案 1 :(得分:1)

我也被这类问题所困扰。现在我认为编写这样的多态函数是不可能的。

我们可以写

alterArray :: (Ix i, IArray b e, IArray a e, MArray a2 e m) => 
              (a2 i e -> m a1) -> a i e -> m (b i e)
alterArray alter ua = do
    mua <- thaw ua 
    alter mua
    freeze mua

或者

alterUArrayST :: (Ix i, IArray UArray e, MArray (STUArray s) e (ST s)) => 
                 (STUArray s i e -> ST s ()) -> UArray i e -> ST s (UArray i e)
alterUArrayST alter ua = do
    mua <- thaw ua 
    alter mua
    freeze mua

但是如果我们要摆脱ST,我们必须编写一些特定类型的版本,例如。

alterUArrayInt :: (forall s. STUArray s Int Int -> ST s ()) -> UArray Int Int -> UArray Int Int
alterUArrayInt alter ua = runST $ do
    mua <- thaw ua 
    alter mua
    freeze mua

alterUArrayFloat :: (forall s. STUArray s Int Float -> ST s ()) -> UArray Int Float -> UArray Int Float
alterUArrayFloat alter ua = runST $ do
    mua <- thaw ua 
    alter mua
    freeze mua

如果MArray有一个实例MArray (STUArray s) e (ST s),我想我们可以编写这样的多态函数。不幸的是,MArray没有这样的实例。

yairchu在https://stackoverflow.com/a/2244281/779412中针对此类问题提供了另一种解决方法。

答案 2 :(得分:0)

将类型声明更改为

alterUArray :: (Ix i, Ord e) => 
               (forall s. STUArray s i e -> ST s ()) -> UArray i e -> UArray i e

并从thaw ua中删除类型注释。

您需要启用RankNTypes扩展程序(或Rank2Types,虽然已弃用)(但是您不需要这样做才能使用runST吗?我忘了)

说明:您的原始类型声明等同于

alterUArray :: (Ix i, Ord e) => 
               forall s. (STUArray s i e -> ST s ()) -> UArray i e -> UArray i e

这意味着alterUArray的来电者可以选择s。我改变的类型坚持认为alterUArray可以自己选择s。然后,您的代码会将s的选择推迟到runST;它的类型坚持认为它可以做出选择。