Ответ 1
Простейшая вещь, которую вы можете сделать, это
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
a data
вместо обертки newtype
, вы можете расширить ее, указав при необходимости много полей в константе.