我正在建模关系数据库的类型信息。我想建立以下图表:
顶点是表格。
对于A中的每个列,表A到表B都存在一个边,这是B的外键。
这是我用于构建图表的初始数据。
newtype TableName = TableName T.Text
newtype ColName = ColName T.Text
newtype ColType = ColType T.Text
type DBInfo = H.HashMap TableName TableInfo
type TableInfo = H.HashMap ColName ColInfo
data ColInfo = CISimple ColType
-- Forms the basis for building the graph
| CIFKey ColType TableName -- col type, referenced table
getDBInfo :: IO (DBInfo)
这些是我期待的图形结构的类型。
data SafeColInfo = SCISimple ColType
-- This captures an edge between two tables
| SCIFKey ColType SafeTableInfo
type SafeTableInfo = H.HashMap TableName SafeColInfo
type SafeDBInfo = ..
我想写这个函数:
convDBInfo :: DBInfo -> Either String SafeDBInfo
convDBInfo
应该构建上面的图表。通过在t
中查找(CIFKey ctype t)
,可以找到有关任何外键t
DBInfo
的信息。如果未找到,则输入数据不一致并且是错误。
在带引用的命令式语言中,这是相当简单的。但是,我想不出在Haskell中解决这个问题的方法。据我所知,这看起来非常适合“绑结”范例,但我似乎无法绕过它。我该如何写这个函数?
答案 0 :(得分:3)
我们可以通过以下方式打结:
convDBInfo :: DBInfo -> SafeDBInfo
convDBInfo dbi = safeDbi where
safeDbi = H.map (H.map go) dbi
go (CIFKey colName tblName) = SCIFKey colName $ safeDbi H.! tblName
go (CISimple colName) = SCISimple colName
我们通过映射输入中的列条目来获得safeDbi
。重要的一点是,我们在safeDbi
的定义中从go
查找表格,因此SCIFKey
- s中的表格将来自生成的hashmap。
示例:
foo :: DBInfo
foo = H.fromList [
("table1", H.fromList [
("col1", CIFKey "a" "table2")
]),
("table2", H.fromList [
("col2", CIFKey "b" "table1")
])
]
bar = convDBInfo foo
SCIFKey _ tbl2 = (bar H.! "table1") H.! "col1"
SCIFKey name _ = tbl2 H.! "col2"
-- name == "b"
请注意,虽然棘手的数据结构通常不方便使用,因为它们与懒惰的无限结构无法区分,所以当我们想要打印,序列化或比较它们时,我们必须给它额外的思考。大多数情况下,显式键控图表更易于使用,性能也不会更差。