2013-04-09 25 views
1

我正在測試一些代碼,用於我正在做的一個小實驗,但在一開始我就碰到了一個我看不到如何修復的障礙。驅動程序的多態函數成員的模糊類型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 

好了,所以這是一些測試代碼,驅動程序的功能會得到多少比這更復雜,但即使在這樣的測試中,我得到的錯誤「曖昧類型的約束變量‘A0’:(場A0)所以編譯器的確看到fieldName被應用到了一個字段上,但顯然它想要一個更具體的類型在這裏,我想讓函數保持多態使得pgsqlDriver不是一個具體的類嗎?

但是這個想法是這些函數是多態的,這就是我在這裏選擇使用GADT的原因,所以我可以將參數的類型約束放在這些驅動函數中,而不必在每個實例化中重複它們。該計劃將是定義的數據庫驅動程序可以與任何Field和Table實例一起工作。這可以不完成,我的DatabaseDriver類型也必須是一個類型類?

回答

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,以及如何tablefield必須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 
+0

這兩個都不適用於我。沒有單聲道限制編譯指示可以清除一些錯誤,但它仍然在定義上顯示錯誤,並添加了一個簽名不會清除該錯誤。 但我注意到,你給一個函數的簽名,而psqlDriver實際上只是一個記錄。 我懷疑記錄不能有多態性功能,因爲這會使他們「多態」,這對於數據類型來說沒有意義(我猜?)。 – Jason 2013-04-09 13:31:48

+0

正如目前所寫,'psqlDriver'不是一個記錄,而是一個函數。它仍然需要第二個參數'dbSelect'。記錄絕對可以是多態的。如果你希望'psqlDriver'是'forall a b'類型的。 DatabaseDriver a b',你將需要提供第二個參數,可能命名爲'select'(儘管這個名字沒有關係,而且我只選擇這個名字,因爲第一個參數'insert'的當前名稱)。 – ScootyPuff 2013-04-09 13:43:33

+0

我現在看到了什麼是打算並修改了答案。 – ScootyPuff 2013-04-09 14:01:39