2012-11-29 4 views
15

型クラスの定数値を宣言する方法:私が実装されていない一定の値を使用し、いくつかの実装の機能を持つ型クラス(<code>table</code>)を宣言したい

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 = ?" 

考え方は単純です:私はしたいです

instance StdQueries SomeType where 
    table = "the_constant_value_for_this_type" 

しかし、コンパイラは、次のメッセージで訴えて続けて:ちょうどtableを指定することで、この型クラスをインスタンス化することによって利用可能byId(および他の類似の機能)を取得

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のヘルプなどでトリックすることはできますか?

答えて

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 #-}ためsabaumaのニーズができます。

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" 

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

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

5

tableの定義にはクラスの型変数は何も記載されていないため、使用するtableのバージョンを特定する方法はありません。あなたは、私は本当にこれをやって推薦することはありません

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

を経由して使用することができます

{-# 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" 

:(確かにハック)溶液のようなものがあるかもしれません。あなたが行うことができます

+4

これはあまり間違っていませんが、多くのタイプのクラスはこのようにします。 – leftaroundabout

+0

コードをより読みやすくするためにフォーマットしてください。また、これは本当に機能しますか?今は 'table'を関数にしているようですが、' where sql = ... 'の部分ではそれを使用していません。 – ErikR

+0

テーブルの型は 'a - > String'であり、' String'ではありません。どうして連結しますか? – Satvik

関連する問題