驱动程序GADT的多态函数成员的模糊类型

时间:2013-04-09 11:58:13

标签: haskell polymorphism gadt

我正在测试一些我正在做的小实验的代码,但是刚开始时我遇到了一个障碍,我看不出如何修复。

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类型也必须是一个类型类吗?

1 个答案:

答案 0 :(得分:3)

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

{-# LANGUAGE NoMonomorphismRestriction #-}

到模块文件的顶部。

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

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

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

第三种选择是将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,以及tablefieldab的关系。

预期的翻译是

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