1

我正在尝试设置一个基本端点,它接受一个 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)
4

0 回答 0