我想声明一个类型类,其中包含一些使用未实现的常量值(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
帮助或类似的东西欺骗吗?
答案 0 :(得分: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
值的函数,则必须多次指定该值。
你可以避免这种情况,sabauma需要这样的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
成为data
结构而不是newtype
包装器,您可以扩展它以指定常量中的许多字段(如果需要)。
答案 1 :(得分:5)
问题是table
的定义没有提到类的任何类型变量,因此无法找出要使用的table
版本。一个(诚然是hackish)解决方案可能是这样的:
{-# 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"
不是我真的建议这样做。