@@ -1,3 +1,4 @@
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
@@ -21,6 +22,7 @@ module Reflex.Query.Class
21 , MonadQuery (..)
22 , MonadQuery (..)
22 , tellQueryDyn
23 , tellQueryDyn
23 , queryDyn
24 , queryDyn
24 , mapQuery
26 , mapQuery
25 , mapQueryResult
27 , mapQueryResult
@@ -35,6 +37,9 @@ import Data.Map.Monoidal (MonoidalMap)
35import qualified Data.Map.Monoidal as MonoidalMap
37import qualified Data.Map.Monoidal as MonoidalMap
36import Data.Semigroup (Semigroup(..))
38import Data.Semigroup (Semigroup(..))
37import Foreign.Storable
39import Foreign.Storable
41+import Data.Monoid hiding ((<>))
42+import Control.Applicative
39import Reflex.Class
44import Reflex.Class
@@ -50,6 +55,37 @@ instance (Ord k, Query v) => Query (MonoidalMap k v) where
50 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
55 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
51 crop q r = MonoidalMap.intersectionWith (flip crop) r q
56 crop q r = MonoidalMap.intersectionWith (flip crop) r q
58+-- | the result of two queries is both results.
59+instance (Query a, Query b) => Query (a, b) where
60+ type QueryResult (a, b) = (QueryResult a, QueryResult b)
61+ crop (x, x') (y, y') = (crop x y, crop x' y')
63+-- | Trivial queries have trivial results.
64+instance Query () where
65+ type QueryResult () = ()
68+-- | The result of an absurd query is trivial; If you can ask the question, the
69+-- answer cannot tell you anything you didn't already know.
71+-- 'QueryResult Void = @Void@' seems like it would also work, but that has
72+-- problems of robustness. In some applications, an unasked question can still
73+-- be answered, so it is important that the result is inhabited even when the
74+-- question isn't. Applications that wish to prevent this can mandate that the
75+-- query result be paired with the query: then the whole response will be
76+-- uninhabited as desired.
77+instance Query Void where
78+ type QueryResult Void = ()
81+#if MIN_VERSION_base(4,12,0)
82+-- | We can lift queries into monoidal containers.
83+-- But beware of Applicatives whose monoid is different from (pure mempty, liftA2 mappend)
84+instance (Query q, Applicative f) => Query (Ap f q) where
85+ type QueryResult (Ap f q) = Ap f (QueryResult q)
53-- | QueryMorphism's must be group homomorphisms when acting on the query type
89-- | QueryMorphism's must be group homomorphisms when acting on the query type
54-- and compatible with the query relationship when acting on the query result.
90-- and compatible with the query relationship when acting on the query result.
55data QueryMorphism q q' = QueryMorphism
91data QueryMorphism q q' = QueryMorphism
@@ -116,3 +152,7 @@ queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t
116queryDyn q = do
152queryDyn q = do
117 tellQueryDyn q
153 tellQueryDyn q
118 zipDynWith crop q <$> askQueryResult
154 zipDynWith crop q <$> askQueryResult
156+-- | Use a query morphism to operate on a smaller version of a query.
157+subQuery :: (Reflex t, MonadQuery t q2 m, Monad m) => QueryMorphism q1 q2 -> Dynamic t q1 -> m (Dynamic t (QueryResult q1))
158+subQuery (QueryMorphism f g) x = fmap g <$> queryDyn (fmap f x)