将句柄存储在自定义数据字段中?

时间:2014-03-16 09:41:34

标签: haskell handle

另一个愚蠢的问题=)我有一个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

3 个答案:

答案 0 :(得分:2)

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

data CustomType = CustomType {
    file::Handle
}

现在在ghci中试试这个:

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

因此Handle包含IO类型。您可以使用Monad绑定运算符 从中提取Handle

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

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

ghci> :t b
b :: IO CustomType

bind>>=的类型如下:

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

尝试将m替换为IO,然后获得:

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

这就是为什么你必须在return运算符中使用bind函数,以便进行类型检查。

答案 1 :(得分:2)

我不完全确定你想要什么。如果您不理解涉及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中输入命令,你可以单独输入do - process - 阻止的每一行,如下所示:

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

答案 2 :(得分:1)

所以你的困惑似乎是如何在IO monad中使用值。关于IO monad的事情是它的传染性,所以每当你想用IO值做某事时你都会得到一个IO结果。这可能听起来像是屁股上的痛苦,但实际上它实际上相当不错,因为它使程序的各个部分保持纯净,并让您完全控制何时执行操作。而不是试图摆脱IO monad,你必须学会​​采用haskell方式将函数应用于monadic值。每个monad都是一个仿函数,因此您可以使用fmap将纯函数应用于monadic值。 monad给出的额外功能是它允许你一起加入上下文。因此,如果你有一个IO a和一个IO 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中。所以你的主要功能应该有IO ()这样的签名。在main函数中,您可以使用其他IO值,例如使用do表示它们是纯粹的。 (<-)关键字有点像我们上面谈到的加入​​。它将来自另一个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的东西,它会在完成后关闭句柄。

* 实际上有一种方法,但你不需要知道它,因为你不太可能解决实际需要它的问题,并且它很容易被新人滥用。< / em>的