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
类型参数的目的是允许
表的任何实例
字段的任何实例
,那么第三个选项应该是您的解决方案。我将进一步编辑第三个选项以解释原因。