在ST monad中使用数组冻结的不明确的类型变量

时间:2016-10-12 19:58:06

标签: arrays haskell

以下代码创建数组,初始化,然后返回不可变数组。

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

我已经尝试过注释掉了类型签名和其他变种,到目前为止没有运气。请在思考过程中包含如何解决问题的思考过程。

4 个答案:

答案 0 :(得分:3)

问题是您没有为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 :(得分:3)

您的功能具有以下最常见的类型:

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

其中包含类型类约束IA.IArray a Int。实际上这有点棘手,因为它将第二个参数中的类型数组部分应用于固定类型。 Haskell98不允许这样做;如果您启用-XFlexibleContexts扩展程序,GHC会执行此操作。除此之外,这显然是一个多态类型,但由于createCustomeInitializedArray没有(显式)参数,编译器希望通过constant applicative form将其设为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,然后将其实例化为该状态参数。嗯,这在这个特定的例子中实际上没有问题,但通常它可能是一个令人讨厌的问题(特别是,外部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

这也可以简洁地写成

ping

答案 2 :(得分:0)

只是提醒一下。使用矢量对我来说似乎更容易,需要的知识也更少。

using (var client = new ImapClient ()) {
    // Note: depending on your server, you might need to connect
    // on port 993 using SecureSocketOptions.SslOnConnect
    client.Connect ("imap.server.com", 143, SecureSocketOptions.StartTls);

    // Note: use your real username/password here...
    client.Authenticate ("username", "password");

    // open the Inbox folder...
    client.Inbox.Open (FolderAccess.ReadOnly);

    // search the folder for new messages (aka recently
    // delivered messages that have not been read yet)
    var uids = client.Inbox.Search (SearchQuery.New);

    Console.WriteLine ("You have {0} new message(s).", uids.Count);

    // ...but maybe you mean unread messages? if so, use this query
    uids = client.Inbox.Search (SearchQuery.NotSeen);

    Console.WriteLine ("You have {0} unread message(s).", uids.Count);

    client.Disconnect (true);
}

答案 3 :(得分:0)

您可以使用freezeSTArray中的GHC.Arr而不是freeze来指定模糊类型:

import Data.Array
import Control.Monad.ST
import Data.Array.ST
import qualified Data.Array.IArray as IA
import GHC.Arr(freezeSTArray)

createCustomeInitializedArray = do
  arr <- newArray (0,10) 0 :: ST s (STArray s Int Int)
  -- ...
  freezeSTArray arr