Nik*_*kov 16 haskell typeclass
我想声明一个类型类,它有一些实现的函数,它们使用一个未实现的常量值(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 = ?"
Run Code Online (Sandbox Code Playgroud)
这个想法很简单:我希望byId通过仅指定table:实例化这个类型类来获得(和其他类似的函数).
instance StdQueries SomeType where
table = "the_constant_value_for_this_type"
Run Code Online (Sandbox Code Playgroud)
但编译器不断抱怨以下消息:
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'
Run Code Online (Sandbox Code Playgroud)
这种问题有什么解决方案吗?可以在newtype帮助或其他类似的情况下欺骗?
dav*_*420 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"
Run Code Online (Sandbox Code Playgroud)
这很简单,但如果您有多个需要访问该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"
Run Code Online (Sandbox Code Playgroud)
这里的魔法是类型签名defaultById,强制byId提供table从同一个实例.如果我们提供defaultById :: (StdQueries a, StdQueries b) => Table a -> Int -> QueryM (Maybe b)然后defaultById仍然会编译,但我们仍然会得到一个类似的错误信息给你的问题:编译器将不再知道使用哪个定义table.
通过使Table a一个data结构,而不是newtype包装,可以延长这在不断指定多个领域,如果需要的话.
问题是该定义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"
Run Code Online (Sandbox Code Playgroud)
然后你可以使用它
table (undefined :: SomeType) == "the_constant_value_for_this_type"
Run Code Online (Sandbox Code Playgroud)
不是我真的建议这样做.