ID を受け取り、結合テーブルを通過して、永続的およびスポックを使用して結合されたすべてのレコードを返す基本的なエンドポイントをセットアップしようとしています。実際の実装は次のようになります。
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: これらの関数では、クラスの制約をそれほど大きくする必要がありますか? これらのモナドを構成しようとすると、非常に多くの制約があるため、すぐに疲れてしまうようです。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)