Haskell 驱动程序GADT多态函数成员的不明确类型
我正在为我正在做的一个小实验测试一些代码,但一开始我遇到了一个我不知道如何解决的障碍Haskell 驱动程序GADT多态函数成员的不明确类型,haskell,polymorphism,gadt,Haskell,Polymorphism,Gadt,我正在为我正在做的一个小实验测试一些代码,但一开始我遇到了一个我不知道如何解决的障碍 data DatabaseDriver a b where DatabaseDriver :: (Table table, Field field) => { dbInsert :: table -> [field] -> String , dbSelect :: table -> [field] -> String } -> Databas
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
好的,这是一些测试代码,驱动程序函数会比这复杂得多,但即使在这个测试中,我在约束中得到了错误“不明确类型变量‘a0’:(字段a0)由于使用“fieldName”而产生。因此编译器确实看到fieldName应用于字段,但显然它需要更具体的类型。我想让函数保持多态性会使pgsqlDriver不是一个具体的类
但是这个想法是这些函数是多态的。这就是我选择在这里使用GADT的原因,因此我可以对这些驱动程序函数的参数设置类型约束,而不必在每次实例化中重复它们。计划是,定义的数据库驱动程序可以处理任何字段和表实例。这可以吗如果不这样做,我的DatabaseDriver类型也必须是类型类?这里有三个选项。第一个选项是添加
{-# LANGUAGE NoMonomorphismRestriction #-}
到模块文件的顶部
第二个选项是向psqlDriver添加显式类型签名
psqlDriver :: (Field field, Table table) => (table -> [field] -> String) -> DatabaseDriver a b
所有这一切的原因都有点微妙,更多细节可以找到
第三个选项是将psqlDriver
的定义更改为
psqlDriver = DatabaseDriver insert select
然而,这确实是不明确的-没有理由偏爱表
或字段
的任何特定实例而不是另一个实例。也许您的意思是定义数据库驱动程序
,如下所示
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
}
请注意嵌套的表格字段
,以及表格
和字段
与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
这两个选项对我都不起作用。No mono restrictions pragma确实清除了一些错误,但它仍然显示了定义上的错误,并且添加了一个签名并没有清除这些错误。但是我注意到,你给出了一个函数的签名,而psqlDriver实际上只是一个记录。我怀疑记录不能有多态函数,因为这会使它们“多态”,这是没有意义的(我想是吧?)对于数据类型。当前编写的,
psqlDriver
不是记录,而是一个函数。它仍然需要第二个参数,dbSelect
。记录肯定可以是多态的。如果希望psqlDriver
为所有a b.数据库驱动程序a b的类型,则需要提供第二个参数,即名称合理的select
(虽然这个名称无关紧要,我之所以选择这个名称是因为第一个参数的当前名称insert
)。我现在明白了原意,并修改了答案。哦,天哪,实际上我是想定义select(请注意,函数在where子句中定义,而不是在顶级语句中使用)这是在我的代码中。很抱歉造成混淆。如果数据库驱动程序a b
中的a
和b
类型参数的目的是允许表的任何实例
和字段的任何实例
,那么第三个选项应该是您的解决方案。我将进一步编辑第三个选项以解释原因。