【问题标题】:Store Handle in custom data field?将句柄存储在自定义数据字段中?
【发布时间】:2014-03-16 09:41:34
【问题描述】:

还有一个愚蠢的问题 =) 我有一个带有 Handle 字段的自定义数据类型:

import System.IO

data CustomType = CustomType {
    file::Handle
}

如何设置file 字段?我正在尝试使用这个明显的代码:

let someFile = openFile fileName AppendMode
let object = CustomType {
    file=someFile
}

但是openFile 有一个类型openFile :: FilePath -> IOMode -> IO Handle,所以我遇到了错误

Couldn't match expected type `Handle' with actual type `IO Handle'

那么我怎样才能在这个字段中存储Handle 对象呢?

UPD

我也在尝试这个

data CustomType = CustomType {
    file::IO Handle
}

但是当我使用 hPutStrLn 函数时,这会导致错误

let object = CustomType {
    file=someFile
}
hPutStrLn (file object)

错误信息是:

 Couldn't match expected type `Handle' with actual type `IO Handle'
    In the return type of a call of `file'
    In the first argument of `TO.hPutStrLn', namely `(file object)'
    In a stmt of a 'do' block:
      TO.hPutStrLn (file object) text

【问题讨论】:

  • file记录中使用IO Handle类型怎么样?
  • 没关系,但是我不知道如何使用hPutStrLn(我正在尝试hPutStrLn (file object),其中对象是CustomType
  • 您必须使用IO monadfile object >>= \h -> hputStrLn h "someline"。那对你有用吗?我可以在下面给出更长的解释作为答案。
  • @DannyNavarro 抱歉回复太晚了。是的,它对我有用。但是更正确的选择是什么——存储IO HandleHandle 记录?另外,我想,我必须了解更多关于 IO monad - 但我很高兴看到你用更长的解释来回答 =)
  • 在你的类型中使用IO Handle 会是一个快速而肮脏的解决方案,但一般来说,在Haskell 中,最好使IO 代码尽可能的精简。这样你就可以利用 Haskell 的纯粹性。所以在你的类型中只使用Handle,如下面的答案所示,我认为它更好。

标签: haskell handle


【解决方案1】:

我不完全确定你想要什么。如果您不了解涉及IO 类型不匹配的类型错误,您可能应该先阅读 Haskell 中的 IO 简介。无论如何,这是有效的代码:

import System.IO

data CustomType = CustomType {
    file :: Handle
  }

fileName :: FilePath
fileName = "foo"

process :: IO ()
process = do
  someFile <- openFile fileName AppendMode
  let object = CustomType { file = someFile }
  hPutStrLn (file object) "abc"
  hClose (file object)

如果您想在 GHCi 中键入命令,可以在 process 操作中单独输入 do-block 的每一行,如下所示:

GHCi> someFile <- openFile fileName AppendMode
GHCi> let object = CustomType { file = someFile }
GHCi> hPutStrLn (file object) "abc"
GHCi> hClose (file object)

【讨论】:

    【解决方案2】:

    所以你已经创建了这样的类型:

    data CustomType = CustomType {
        file::Handle
    }
    

    现在在 ghci 中试试这个:

    ghci> let a = openFile "someFile.txt" AppendMode
    ghci> :t a
    a :: IO Handle
    

    所以HandleIO 类型包裹。您可以使用 Monad 绑定运算符 从中提取Handle

    ghci> let b = a >>= \handle -> return $ CustomType handle
    

    return 函数将 CustomType 再次包装在 IO monad 中。您可以在 ghci 中再次验证这一点:

    ghci> :t b
    b :: IO CustomType
    

    bind&gt;&gt;=的类型是这样的:

    ghic> :t (>>=)
    (>>=) :: Monad m => m a -> (a -> m b) -> m b
    

    尝试将m 替换为IO,你会得到:

    (>>=) :: IO a -> (a -> IO b) -> IO b
    

    这就是为什么您必须将return 函数与bind 运算符一起使用,以便它进行类型检查。

    【讨论】:

      【解决方案3】:

      所以您的困惑似乎在于如何在 IO monad 中使用值。 IO monad 的特点是它具有传染性,所以无论何时你想用一个 IO 值做某事,你都会得到一个 IO 结果。这可能听起来很痛苦,但实际上它非常好,因为它使程序的各个部分保持纯净,并让您完全控制何时执行操作。与其试图摆脱 IO monad,您必须学会接受将函数应用于 monadic 值的 haskell 方式。每个 monad 都是一个函子,因此您可以使用 fmap 将纯函数应用于 monadic 值。 monad 提供的额外功能是它允许您将上下文连接在一起。因此,如果您有IO aIO b,您可以加入 IO 以获得IO (a, b)。我们可以利用这些知识来解决您的问题:

      openFile 有签名:

      openFile :: FilePath -> IOMode -> IO Handle
      

      如上所述,没有办法*从 Handle 中删除 IO,因此您唯一能做的就是将您的类型也放入 IO monad。例如,您可以制作使用 fmap 将您的对象应用到 IO Handle 的函数:

      createMyObject :: FilePath -> IO CustomType
      createMyObject fp = CustomType `fmap` openFile fp AppendMode
      

      现在你有了你的对象,但是它在一个 IO monad 中,那么你如何使用它呢?应用程序的最外层始终位于 IO monad 中。所以你的 main 函数应该有一个像IO () 这样的签名。在 main 函数中,您可以使用其他 IO 值,例如使用 do 表示法它们是纯的。 (&lt;-) 关键字有点像我们上面谈到的 join。它将另一个 IO 中的值提取到当前 IO 中:

      main :: IO ()
      main = do
          myObjectPure <- createMyObject "someFilePath.txt"
      
          let myHandle :: Handle -- No IO!
              myHandle = file myObjectPure
      
          -- Now you can use the functions that takes a pure handle:
          hPutStrLn myHandler "Yay"
      

      顺便说一句,您可能不应该直接以这种方式使用 Handle,因为您很容易忘记关闭它。最好使用withFile 之类的东西,它会在完成后为您关闭句柄。

      *其实有一种方法,但你还不需要知道它,因为你不太可能解决真正需要它的问题,而且对于新手来说太容易滥用了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-10-26
        • 1970-01-01
        • 1970-01-01
        • 2010-10-31
        • 2016-08-02
        • 2017-07-05
        • 1970-01-01
        相关资源
        最近更新 更多