1

我正在为我正在做的一个小实验测试一些代码,但一开始我遇到了一个我不知道如何解决的障碍。

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 的原因,因此我可以对这些驱动程序函数的参数进行类型约束,而不必在每次实例化中重复它们。计划是定义的数据库驱动程序可以与任何 Field 和 Table 实例一起使用。这不能简单地完成,我的 DatabaseDriver 类型也必须是一个类型类吗?

4

1 回答 1

3

这里有三个选项。第一个选项是添加

{-# LANGUAGE NoMonomorphismRestriction #-}

到模块文件的顶部。

第二个选项是向 psqlDriver 添加显式类型签名。

psqlDriver :: (Field field, Table table) => (table -> [field] -> String) -> DatabaseDriver a b

这一切的原因有点微妙,更多细节可以在这里找到

第三个选项是更改 to 的psqlDriver定义

psqlDriver = DatabaseDriver insert select

然而,这确实是模棱两可的——没有理由偏爱任何特定实例TableField优于另一个实例。也许你的意思是定义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与orfield没有关系。ab

预期的翻译是

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
于 2013-04-09T12:37:03.650 回答