我正在测试一些我正在做的小实验的代码,但是刚开始时我遇到了一个障碍,我看不出如何修复。
data DatabaseDriver a b where
DatabaseDriver :: (Table table, Field field) => {
dbInsert :: table -> [field] -> String
, dbSelect :: table -> [field] -> String
} -> DatabaseDriver a b
class Table a where
tableName :: a -> String
class Field a where
fieldName :: a -> String
fieldValue :: a -> String
psqlDriver = DatabaseDriver insert select
where
insert t fs = "insert into " ++ tableName t ++ " (" ++ fieldNames fs ++ ") values (" ++ fieldValues fs ++ ")"
select t fs = "select " ++ fieldNames fs ++ " from " ++ tableName t
fieldNames = joinComma fieldName
fieldValues = joinComma fieldValue
joinComma f = foldl (\a n -> a ++ ", " ++ n) "" . map f
好的,所以这是一些测试代码,驱动程序函数会比这复杂得多,但即使在这个测试中,我在约束中得到错误“Ambiguous type variable'a0':(字段a0)因使用而产生`fieldName'。所以编译器确实看到fieldName应用于一个字段,但显然它想要一个更具体的类型。我想这些函数仍然是多态的,这使得pgsqlDriver不是一个具体的类?
但是这个想法是这些函数是多态的。这就是我选择在这里使用GADT的原因,所以我可以将参数的类型约束放到这些驱动程序函数中,而不必在每个实例化中重复它们。计划是定义的数据库驱动程序可以与任何Field和Table实例一起使用。这可以简单地完成,我的DatabaseDriver类型也必须是一个类型类吗?
答案 0 :(得分:3)
这里有三个选项。第一个选项是添加
{-# LANGUAGE NoMonomorphismRestriction #-}
到模块文件的顶部。
第二个选项是向psqlDriver添加显式类型签名。
psqlDriver :: (Field field, Table table) => (table -> [field] -> String) -> DatabaseDriver a b
所有这一切的原因有点微妙,可以找到更多细节here
第三种选择是将psqlDriver
的定义更改为
psqlDriver = DatabaseDriver insert select
然而,这确实含糊不清 - 没有理由更喜欢Table
或Field
的任何特定实例。也许你的意思是如下定义DatabaseDriver
。
data DatabaseDriver table field where
DatabaseDriver :: (Table table, Field field) => {
dbInsert :: table -> [field] -> String
, dbSelect :: table -> [field] -> String
} -> DatabaseDriver table field
如果将DatabaseDriver
的原始定义重写为ADT,则更为明显。
正如目前在问题中所写,ADT的翻译是
{-# LANGUAGE ExistentialQuantification #-}
data DatabaseDriver a b
= forall table field .
(Table table, Field field) => DatabaseDriver
{ dbInsert :: table -> [field] -> String
, dbSelect :: table -> [field] -> String
}
请注意嵌套的forall table field
,以及table
和field
与a
或b
的关系。
预期的翻译是
data DatabaseDriver table field
= (Table table, Field field) => DatabaseDriver
{ dbInsert :: table -> [field] -> String
, dbSelect :: table -> [field] -> String
}
或者很可能
data DatabaseDriver table field
= DatabaseDriver
{ dbInsert :: table -> [field] -> String
, dbSelect :: table -> [field] -> String
}
在DatabaseDriver
的定义中使用类型类约束不允许您从DatabaseDriver
的任何使用中删除类型类约束,特别是psqlDriver
。在上面的两个ADT翻译中,psqlDriver
的类型是
psqlDriver :: (Table table, Field field) => DatabaseDriver table field