很抱歉要问这个可能很愚蠢的问题,但是回到Haskell做一些从一个数据库包到另一个数据包的转换,我发现自己对如何正确地做这件事感到有些困惑。
在Database.SQLite3
模块中,有execWithCallback
类型
execWithCallback :: Database -> Text -> ExecCallback -> IO ()
现在,回调被定义为
type ExecCallback = ColumnCount -> [Text]-> [Maybe Text] -> IO ()
即类型ExecCallback
的函数
我愚蠢的测试代码编译并正确运行:
{-# LANGUAGE OverloadedStrings #-}
import Database.SQLite3
import Data.Text
cb :: ColumnCount -> [Text] -> [Maybe Text] -> IO ()
cb n cnl ct = do print $ cnl !! 1
return ()
main = do
dh <- open "fileinfo.sqlite"
execWithCallback dh "select * from files;" cb
close dh
然后,这个类型的重点是什么?而且,如何指定cb
是ExecCallback
??
答案 0 :(得分:2)
在Haskell中,使用type
定义类型同义词。在您的示例中,这意味着ExecCallback
只是类型ColumnCount -> [Text]-> [Maybe Text] -> IO ()
的别名,它们是可互换的。
您可以更改以下行
cb :: ColumnCount -> [Text] -> [Maybe Text] -> IO ()
cb n cnl ct = do print $ cnl !! 1
return ()
到
cb :: ExecCallback
cb n cnl ct = do print $ cnl !! 1
return ()
一切都会按原样运作。它可以使您的代码更短,更易读。
另一个好例子是
type String = [Char]
in Prelude
。我敢打赌,在大多数情况下,您通常会使用String
代替[Char]
。但你完全可以自由使用它们。
另一个(完全不相关的)示例是conduit
包,其中一些type synonyms产生了重大差异:
type Sink i = ConduitM i Void
type Consumer i m r = forall o. ConduitM i o m r
对于任何类型i
的值的接收器,Sink i
似乎比ConduitM i Void
更具可读性。与Consumer
相同。