2015-11-06 58 views
3

此代码编译没有问题:如何在ST monad中创建一个多态的unboxed数组?

import Control.Monad.ST (ST) 
import Data.Array.MArray (MArray) 
import Data.Array.Unboxed (UArray) 
import Data.Array.ST (runSTUArray, newArray, STUArray) 

new :: Double -> UArray Int Double 
new a = runSTUArray (newArray (0, 9) a) 

但是,这样的:

new :: e -> UArray Int e 
new a = runSTUArray (newArray (0, 9) a) 

失败,正如人们所期望的那样,出现错误:

No instance for (MArray (STUArray s) e (ST s)) 
    arising from a use of ‘newArray’ 
In the first argument of ‘runSTUArray’, namely 
    ‘(newArray (0, 9) a)’ 
In the expression: runSTUArray (newArray (0, 9) a) 
In an equation for ‘new’: new a = runSTUArray (newArray (0, 9) a) 

然而,增加的类型级约束将无济于事,因为将类型签名更改为

new :: (MArray (STUArray s) e (ST s)) => e -> UArray Int e 

仍然会失败

Could not deduce (MArray (STUArray s0) e (ST s0)) 
from the context (MArray (STUArray s) e (ST s)) 
    bound by the type signature for 
      new :: MArray (STUArray s) e (ST s) => e -> UArray Int e 
    at pilot.hs:7:8-58 
The type variable ‘s0’ is ambiguous 
In the ambiguity check for the type signature for ‘new’: 
    new :: forall e s. 
     MArray (STUArray s) e (ST s) => 
     e -> UArray Int e 
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes 
In the type signature for ‘new’: 
    new :: (MArray (STUArray s) e (ST s)) => e -> UArray Int e 

什么办法,使这项工作?


编辑:发现了这一点已在HaskellHaskell-Cafe邮件列表的讨论,从here采取这个最小的解决方案:

-- https://mail.haskell.org/pipermail/haskell/2005-August/016354.html 
{-# LANGUAGE Rank2Types, FlexibleContexts #-} 

import Control.Monad.ST (ST) 
import Data.Array.MArray (MArray) 
import Data.Array.Unboxed (UArray, Ix) 
import Data.Array.ST (runSTUArray, newArray, STUArray) 

new :: UArrayElement e => e -> UArray Int e 
new a = case freezer of 
    Freezer runSTUArray' -> runSTUArray' $ (newArray (0, 9) a) 

data Freezer i e = Freezer 
    ((forall s. MArray (STUArray s) e (ST s) => ST s (STUArray s i e)) 
    -> UArray i e) 

class UArrayElement e where 
    freezer :: Ix i => Freezer i e 

instance UArrayElement Bool where freezer = Freezer runSTUArray 
instance UArrayElement Char where freezer = Freezer runSTUArray 
instance UArrayElement Double where freezer = Freezer runSTUArray 
+1

恐怕这只是不可能,因为'Array'没有明确的类用于不可用的类型。 ['Vector' has](http://hackage.haskell.org/package/vector-0.11.0.0/docs/Data-Vector-Unboxed.html)。 – leftaroundabout

+0

@leftaroundabout我的用例涉及二维索引,这是笨拙的矢量 –

回答

7

下面似乎工作。我不知道它是否可以最小化。一个缺点是您需要为每个MArray实例添加一个Unboxable实例,您可以使用该实例 - 但至少要感谢DefaultSignatures,您不必编写任何实际代码。我已经包含一个Int的实例来展示我的意思。

{-# LANGUAGE ScopedTypeVariables #-} 
{-# LANGUAGE DefaultSignatures #-} 
{-# LANGUAGE FlexibleContexts #-} 
import Control.Monad.ST 
import Data.Constraint 
import Data.Array.Unboxed 
import Data.Array.ST 

class Unboxable e where 
    unboxable :: Dict (MArray (STUArray s) e (ST s)) 
    default unboxable :: MArray (STUArray s) e (ST s) => Dict (MArray (STUArray s) e (ST s)) 
    unboxable = Dict 

new :: forall e. Unboxable e => e -> UArray Int e 
new e = runSTUArray build where 
    build :: forall s. ST s (STUArray s Int e) 
    build = case unboxable :: Dict (MArray (STUArray s) e (ST s)) of 
     Dict -> newArray (0, 9) e 

instance Unboxable Int 

Dict类型来自爱德华Kmett的constraints包。

5

由于考虑到我们已经使用constraints我们可以利用的Data.Constraint.Forall让所有的Unboxable实例在一个细化到丹尼尔·瓦格纳的回答,一举:

{-# LANGUAGE 
    ScopedTypeVariables, TypeOperators, 
    MultiParamTypeClasses, FlexibleContexts, 
    FlexibleInstances, UndecidableInstances #-} 

import Control.Monad.ST (ST) 
import Data.Array.MArray (MArray) 
import Data.Array.Unboxed (UArray) 
import Data.Array.ST (runSTUArray, newArray, STUArray) 

import Data.Constraint 
import Data.Constraint.Forall 

class MArray (STUArray s) e (ST s) => Unboxable e s 
instance MArray (STUArray s) e (ST s) => Unboxable e s 

new :: forall e. Forall (Unboxable e) => e -> UArray Int e 
new a = runSTUArray build where 
    build :: forall s. ST s (STUArray s Int e) 
    build = case inst :: Forall (Unboxable e) :- Unboxable e s of 
    Sub Dict -> newArray (0, 9) a 

-- can be specialized to Double 
new' :: Double -> UArray Int Double 
new' = new 
+3

我想我有义务指出['Forall'是不健全的](https://github.com/ekmett/constraints/issues/11),并且会在下一个版本的约束条件中受到很大限制,可能会导致它不再用于避免个别情况。 –

+0

@ØrjanJohansen,是'Forall'不完善的重叠实例和封闭类型家族的可疑特征? – dfeuer

+2

@dfeuer [开放类型的家庭是足够的](http://oerjan.nvg.org/haskell/Forall/UC3.hs)。 –