我正在尝试设置一个基本端点,它接受一个 id 并通过一个连接表以使用持久性和 spock 返回所有连接的记录,一个工作实现看起来像这样。
get ("songs" <//> var) $ \id -> do
song <- getRecord404 $ SongKey id
tags <- runSQL $ select $
from $ \(tag `InnerJoin` songTag) -> do
on (songTag ^. SongTagTagId ==. tag ^. TagId)
where_ (songTag ^. SongTagSongId ==. val (SongKey id))
return (tag)
json $ object ["song" .= song, "tags" .= tags]
getRecord404 :: (PersistEntity a, PersistEntityBackend a ~ SqlBackend, MonadIO m0, Monad m, ActionCtxT ctx m0 ~ m, HasSpock m, SpockConn m ~ SqlBackend) => Key a -> m (Entity a)
getRecord404 k = do
res <- getRecord k
guardNotFound res
guardNotFound :: MonadIO m => Maybe (Entity a) -> ActionCtxT ctx m (Entity a)
guardNotFound Nothing = do
setStatus status404
text "Not found"
guardNotFound (Just a) = return a
问题 1:这些函数的类约束是否必须如此之大?似乎尝试编写这些 monad 会因为有这么多的限制而很快变得厌烦。我已经看到我可以使用 Constraint Kinds 来设置约束同义词,但我觉得可能我做错了什么需要这么多约束。
我还想看看是否可以编写一个更通用的方法来执行连接操作。据推测,具有输入类型和用于连接的表就足够了,编译器可以推断输出类型并且(至少在像 ruby 这样的语言中)您可以检查连接表类型以找到正确的列加入。就像是:
manyThrough :: (Monad m...) => Key a -> [Somehow pass the information about which table to use] -> m [Entity B]
但是,尝试实现这样的功能超出了我的范围。我不确定传递有关用于联接的表的信息的最佳方法是什么。我实现了一个显式传递列的版本(如下),它可以工作,但它再次具有巨大的类约束,并且采用比我想要的更大的方法签名。有没有办法实现上面的签名?
manyThrough :: (PersistEntity t1, PersistEntity t2, PersistField (Key b) , PersistField (Key a), PersistEntityBackend t1 ~ SqlBackend, PersistEntityBackend t2 ~ SqlBackend, SpockConn m ~ SqlBackend, typ2 ~ Key b, typ1 ~ Key a, HasSpock m, SqlSelect (SqlExpr (Entity t2)) (Entity b)) => typ1 -> EntityField t1 typ1 -> EntityField t1 typ2 -> EntityField t2 typ2 -> m [Entity b]
manyThrough key joinId1 joinId2 targetId= runSQL $ select $
from $ \(joinTable `InnerJoin` target) -> do
on (joinTable ^. joinId2 ==. target ^. targetId)
where_ (joinTable ^. joinId1 ==. val (key))
return (target)