我试图设置一个基本端点,它接受一个id并通过一个连接表来使用persistent和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设置约束同义词,但我觉得我可能做错了需要这么多约束。
我还想看看我是否可以编写一个更通用的方法来执行连接操作。 据推测,输入类型和用于连接的表是足够的信息,can编译器可以推断输出类型,并且(至少在类似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)