Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding meta queries to support combinations of queries as queries. #291

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

favilo
Copy link

@favilo favilo commented Mar 28, 2024

Adding structs:

  • AndQuery
  • OrQuery
  • NotQuery
  • AnyQuery
  • AllQuery

- `AndQuery`
- `OrQuery`
- `NotQuery`
- `AnyQuery`
- `AllQuery`
@sminez
Copy link
Owner

sminez commented Apr 14, 2024

I'm not entirely sure I'm convinced by the ergonomics of this or why this being raised as a PR without first raising an issue to discuss the feature request.

@favilo
Copy link
Author

favilo commented Apr 15, 2024

I'm personally using these with an extension trait to make it more ergonomic.

Apologies for not raising an issue first. I've raise #294 just now.

where
X: XConn,
{
fn and(self, other: impl Query<X>) -> AndQuery<Self, impl Query<X>>
Copy link
Author

@favilo favilo Apr 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to replace the impl Query<X> with a specific type Q: Query<X> here, but the compiler complains with

error[E0283]: type annotations needed
   --> src/hooks.rs:127:47
    |
127 |             Titles(ZOOM_TILE_TITLES.to_vec()).not()
    |                                               ^^^
    |
    = note: cannot satisfy `_: XConn`
    = help: the trait `XConn` is implemented for `Conn<C>`
note: required by a bound in `QueryExt::not`
   --> src/hooks.rs:63:8
    |
63  |     X: XConn,
    |        ^^^^^ required by this bound in `QueryExt::not`
...
79  |     fn not(self) -> NotQuery<Self>
    |        --- required by a bound in this associated function
help: try using a fully qualified path to specify the expected types
    |
127 |             <Titles as QueryExt<X>>::not(Titles(ZOOM_TILE_TITLES.to_vec()))
    |             +++++++++++++++++++++++++++++                                 ~

Which would make it much less ergonomic to use.

@sminez
Copy link
Owner

sminez commented Apr 30, 2024

It looks like the structs you are defining aren't being given a consistent set of derives? They should all really be deriving the same common set of Debug, Copy, Clone, PartialEq and Eq.

The compiler issue you're showing here is down to the structs you've defined not having any trait bounds I suspect? Taking the NotQuery as an example:

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct NotQuery<Q>(pub Q);

Here Q can be anything at all. Nothing is constraining it to be only something that implements Query. I've had a quick play around with seeing what might work without resorting to impl Query<X> everywhere and I think something like this works?

/// A query to be run against client windows for identifying specific windows
/// or programs.
pub trait Query<X: XConn> {
    /// Run this query for a given window ID.
    fn run(&self, id: Xid, x: &X) -> Result<bool>;

    /// The negation of this query
    fn not(self) -> NotQuery<X, Self>
    where
        Self: Sized,
    {
        NotQuery {
            inner: self,
            _phantom: std::marker::PhantomData,
        }
    }
}

/// The logical negation of `Q`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct NotQuery<X: XConn, Q: Query<X>> {
    inner: Q,
    _phantom: std::marker::PhantomData<X>,
}

impl<X, Q> Query<X> for NotQuery<X, Q>
where
    X: XConn,
    Q: Query<X>,
{
    fn run(&self, id: Xid, x: &X) -> Result<bool> {
        Ok(!self.inner.run(id, x)?)
    }
}

Exposing the inner Query as public for these structs isn't really that valuable when they should be getting constructed by calling the combinator methods (and becomes actively annoying to do once we need PhantomData). Also, if this is going to be added as a feature from the main Penrose crate itself these should just be methods on the Query trait directly rather than adding an extension trait that then needs to be imported.

All that said, I'm still not convinced that I fully see the utility of the All and Any structs? I can see the logical operator structs being useful for simple combinations, but for anything sufficiently complicated that you start to use a lot of these I think it is probably going to be easier to write explicit queries instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants