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

ここには 3 つのオプションがあります。最初のオプションは追加することです

{-# LANGUAGE NoMonomorphismRestriction #-}

モジュールファイルの先頭に。

2 番目のオプションは、明示的な型シグネチャを psqlDriver に追加することです。

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

このすべての理由は少し微妙です。詳細については、こちらを参照してください

3 番目のオプションは、 の定義を に変更することです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

の元の定義がDatabaseDriverADT として書き直された場合、その理由はより明白になります。

現在質問に書かれているように、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, と howtableおよびは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の使用から型クラス制約を削除することはできません。上記の両方の ADT 変換で、の型はDatabaseDriverpsqlDriverpsqlDriver

psqlDriver :: (Table table, Field field) => DatabaseDriver table field
于 2013-04-09T12:37:03.650 に答える