16

table実装されていない定数値 ( )を使用する関数が実装されている型クラスを宣言したいと考えています。

class FromRow a => StdQueries a where
  table :: String
  byId :: Int -> QueryM (Maybe a)
  byId = fmap listToMaybe . queryM sql . Only
    where sql = read $ "SELECT * FROM " ++ table ++ " WHERE id = ?"

byIdアイデアは単純です: を指定するだけでこの型クラスをインスタンス化することで、(および他の同様の関数) を利用できるようにしたいと考えていますtable

instance StdQueries SomeType where
  table = "the_constant_value_for_this_type"

しかし、コンパイラは次のメッセージで不平を言い続けます:

The class method `table'
mentions none of the type variables of the class StdQueries a
When checking the class method: table :: String
In the class declaration for `StdQueries'

そのような問題の解決策はありますか?助けを借りてだますことはできますnewtypeか?

4

2 に答える 2

17

あなたができる最も簡単なことは、

class FromRow a => StdQueries a where
    byId :: Int -> QueryM (Maybe a)

defaultById :: FromRow a => String -> Int -> QueryM (Maybe a)
defaultById table = fmap listToMaybe . queryM sql . Only
    where sql = read $ "SELECT * FROM " ++ table ++ " WHERE id = ?"

instance StdQueries SomeType where
    byId = defaultById "the_constant_value_for_this_type"

これは簡単ですが、table値にアクセスする必要がある関数が複数ある場合は、その値を複数回指定する必要があります。

あなたはそれを避けることができ、サバウマの必要性は次のようにundefinedなり{-# LANGUAGE ScopedTypeVariables #-}ます:

newtype Table a = Table String

class FromRow a => StdQueries a where
    table :: Table a
    byId :: Int -> QueryM (Maybe a)
    byId = defaultById table

defaultById :: StdQueries a => Table a -> Int -> QueryM (Maybe a)
defaultById (Table table) = fmap listToMaybe . queryM sql . Only
    where sql = read $ "SELECT * FROM " ++ table ++ " WHERE id = ?"

instance StdQueries SomeType where
    table = Table "the_constant_value_for_this_type"

ここでの魔法は の型シグネチャであり、同じインスタンスからdefaultByIdを強制的byIdに提供します。table提供しdefaultById :: (StdQueries a, StdQueries b) => Table a -> Int -> QueryM (Maybe b)た場合defaultByIdでもコンパイルは行われますが、質問にあるものと同様のエラー メッセージが表示されます。コンパイラは、table使用する の定義を認識できなくなります。

ラッパーの代わりに構造体を作成Table aすることにより、これを拡張して、必要に応じて定数内の多くのフィールドを指定できます。datanewtype

于 2012-11-29T16:35:28.933 に答える
5

問題は、 の定義がtableクラスの型変数のいずれにも言及していないため、table使用する のバージョンを特定する方法がないことです。(確かにハック的な) 解決策は、次のようなものです。

{-# LANGUAGE ScopedTypeVariables #-}
class FromRow a => StdQueries a where
  table :: a -> String
  byId :: Int -> QueryM (Maybe a)
  byId = fmap listToMaybe . queryM sql . Only
    where sql = read $ "SELECT * FROM " ++ table (undefined :: a) ++ " WHERE id = ?"

instance StdQueries SomeType where
    table = const "the_constant_value_for_this_type"

その後、これを介して使用できます

table (undefined :: SomeType) == "the_constant_value_for_this_type"

これを行うことを本当にお勧めするわけではありません。

于 2012-11-29T16:03:39.493 に答える