2016-10-12 71 views
1

以下代碼創建數組,初始化,然後返回不可變數組。ST monad中使用數組凍結的模糊類型變量

import Data.Array 
import Control.Monad.ST 
import Data.Array.ST 
import qualified Data.Array.IArray as IA 

createCustomeInitializedArray = do 
    arr <- newArray (0,10) 0 :: ST s (STArray s Int Int) 
    -- some mutation writeArray arr 0 1 etc 
    iarr <- freeze arr -- :: ST s (IA.IArray Array s) 
    return iarr 

iarr <- freeze arr原因錯誤類型

> No instance for (IA.IArray b0 Int) arising from a use of `freeze' 
>  The type variable `b0' is ambiguous 

我已經試過到目前爲止註釋掉類型的簽名和其他變化,沒有運氣。請包括涉及如何解決答案的思考過程。

回答

2

你有以下的最普遍的類型:

createCustomeInitializedArray :: IA.IArray a Int => ST s (a Int Int) 

這有一個類型類的約束IA.IArray a Int在裏面。實際上這有點棘手,因爲它將類型類在第二個參數中部分應用於固定類型。 Haskell98不允許這樣;如果啓用-XFlexibleContexts擴展名,GHC會執行此操作。除此之外,這顯然是一種多態類型,但由於createCustomeInitializedArray沒有(顯式)參數,因此編譯器希望通過dreaded monomorphism restriction將其設置爲。即它拒絕推斷多態類型,你需要用明確的簽名來請求它。採用頂級的簽名是一個非常好的主意,無論如何,這樣做的:

{-# LANGUAGE FlexibleContexts #-} 
createCustomeInitializedArray :: IA.IArray a Int => ST s (a Int Int) 
createCustomeInitializedArray = do 
    arr <- newArray (0,10) 0 :: ST s (STArray s Int Int) 
    -- ... 
    iarr <- freeze arr 
    return iarr 

我想補充一點,你有newArray (0,10) 0這個地方簽名不辦得是什麼樣子:Haskell的類型變量不範圍,所以s那裏不知道s從邊遠國家;以下是更忠實於編譯器看到的是:

createCustomeInitializedArray :: IA.IArray a Int => ST s (a Int Int) 
createCustomeInitializedArray = do 
    arr <- newArray (0,10) 0 :: ST s₁ (STArray s₁ Int Int) 
    -- ... 

因此,它使當地的行動從外部s完全獨立的多態,只有那麼它實例到該狀態的說法。嗯,在這個特例中這實際上沒有問題,但通常它可能是一個討厭的問題(特別是外層的約束不會傳播到內層)。因此,這是代碼,我建議:

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

createCustomeInitializedArray :: ∀ s a . IA.IArray a Int => ST s (a Int Int) 
createCustomeInitializedArray = do 
    arr <- newArray (0,10) 0 :: ST s (STArray s Int Int) 
    -- ... 
    iarr <- freeze arr 
    return iarr 

這也可以更簡明地寫一點作爲

arr :: STArray s Int Int <- newArray (0,10) 0 
2

問題是你沒有給createCustomInitializedArray簽名。由於Monomorphism Restriction Haskell將嘗試推斷createCustomInitializedArray的具體類型。但它不能,因爲freeze在結果中是多態的。

有四種方法來解決這個問題:

  1. createCustomInitializedArray提供一個單態類型簽名。例如:ST s (Array Int Int)
  2. 註釋iarr一個具體類型是這樣的:

    return (iarr :: Array Int Int) 
    
  3. createCustomInitilizedArray提供一個多態型標誌。

    {-# LANGUAGE FlexibleContexts #-} 
    ... 
    createCustomInitializedArray :: IA.IArray a Int => ST s (a Int Int) 
    ... 
    
  4. 啓用NoMonomorphismRestriction語言擴展讓Haskell推斷多態類型。我認爲這將是最不利的選擇,因爲無論如何,您應該爲所有頂級功能提供類型簽名。
+1

僅添加'iarr < - freeze arr :: ST s(Array Int Int)'不起作用。其他解決方案正在工作 –

+1

正確的,你不可以爲'ST'行爲做一個單形簽名(因爲'runST'是rank-2多態的,所以不會變得無法使用)。 – leftaroundabout

0

只是一個提醒。使用矢量對我來說似乎更容易,所需知識更少。

import qualified Data.Vector.Unboxed.Mutable as M 
import Data.Vector.Unboxed 
import Control.Monad.ST 
import Control.Monad 

createCustomeInitializedArray :: ST s (Vector Int) 
createCustomeInitializedArray = do 
    arr <- M.new 10 
    -- ... 
    iarr <- freeze arr 
    return iarr 
相關問題