0

Problem

I have a feeling that the reason for this question is going to ultimately come down to me just being new at/not understanding Haskell, so I'll try to be as detailed as I can with my description, to make it somewhat generic.

I am using a custom XMonad config as my window manager, defined in a file xmonad.hs.

The XMonad manageHook is what I use to handle when certain applications need to open in a certain type of window or on a certain workspace, etc. https://hackage.haskell.org/package/xmonad-0.17.0/docs/XMonad-ManageHook.html

Currently, a bunch of lines in my manageHook in xmonad.hs look like this:

className =? "xxxx" --> doSomething

Here is my current understanding of the line above:

These expressions involve the value of className, defined in XMonad.Hooks.ManageHook, which is of the Query type (Query [Char], to be more specific). https://hackage.haskell.org/package/xmonad-0.17.0/docs/XMonad-ManageHook.html

Query is defined in XMonad.Core. https://hackage.haskell.org/package/xmonad-0.17.0/docs/XMonad-Core.html#t:Query

=? takes the Query [Char] on the left and the [Char] literal on the right and returns a value of type Query Bool; the value will be True if the [Char]s are equal and False otherwise (I think?)

--> takes the Query Bool on the left and an action on the right and executes the action if the value of the Query Bool is True.

https://hackage.haskell.org/package/xmonad-0.17.0/docs/XMonad-ManageHook.html

However, this is only useful to me when I know the exact class name of an application I want to apply a rule to, which is not always the case. So, I took a look at xmonad-contrib to get some ideas as to what other functions I could define.

In XMonad.Hooks.ManageHelpers (https://hackage.haskell.org/package/xmonad-contrib-0.17.0/docs/XMonad-Hooks-ManageHelpers.html), there is ^?, which would be used like below:

className ^? "xxxx" --> doSomething

From my understanding, ^? takes the Query [Char] on the left and the [Char] literal on the right and returns a value type of Query Bool; the value will be True if the [Char] on the left isPrefixOf the [Char] on the right and False otherwise (I think?)

What I would like to create is a new function that is similar, but reversed. Something like,

"xxxx" ?^ className --> doSomething

where ?^ takes the [Char] literal on the left and the Query [Char] on the right and returns a value of type Query Bool; the value should be True if the [Char] on the left isPrefixOf the [Char] on the right and False otherwise. (In other words, I want to define a new function that checks if some string literal is a prefix of the class name, rather than checking if the class name is a prefix of some string literal.)

Initially, I thought this should be easy, but looking at the source code for ^?, I realized there must be something I am fundamentally missing about my understanding of Haskell.

Here is the definition of ^?:

(^?) :: (Eq a, Functor m) => m [a] -> [a] -> m Bool
q ^? x = fmap (`isPrefixOf` x) q

https://hackage.haskell.org/package/xmonad-contrib-0.17.0/docs/src/XMonad.Hooks.ManageHelpers.html#%5E%3F

What I do not understand is how I would write a function that is a reversed version of this. This definitely won't work:

(?^) :: (Eq a, Functor m) => [a] -> m [a] -> m Bool
x ?^ q = fmap (`isPrefixOf` q) q

yet I do not understand why. What property does Query have that (Query [a]) isPrefixOf ([a]) is acceptable, but not ([a]) isPrefixOf (Query [a])?

Furthermore, how can I go about defining a function with the desired behavior? I am still pretty new to Haskell, so I do not know where to start.

Solution

I was closer than I thought. See the answer for more details, but the solution was just to fix my syntax for the invocation of isPrefixOf (oops):

x ?^ q = fmap (x `isPrefixOf`) q
Oh Fiveight
  • 299
  • 1
  • 9
  • 1
    Neither ```(undefined :: Query [A]) `isPrefixOf` (undefined :: [A])``` nor ```(undefined :: [A]) `isPrefixOf` (undefined :: Query [A])``` is acceptable! The `fmap` changes everything; it takes a non-`Query` function and lifts it into `Query`-land, having type `fmap :: (a -> b) -> Query a -> Query b`. – Daniel Wagner Apr 12 '22 at 17:52
  • @DanielWagner Thanks for the clarification! The issue with my code ended up being unrelated to `fmap`, but I'm glad I understand the function better now, because it is a large part of what was confusing me; I see it used frequently in Haskell code, but I never properly understood when it is necessary. – Oh Fiveight Apr 12 '22 at 18:11

1 Answers1

1

You gotta switch the arguments to isPrefixOf!

x ?^ q = fmap (x `isPrefixOf`) q

Other spellings are possible, such as this version that doesn't use the infix form of isPrefixOf:

x ^? q = isPrefixOf x <$> q

((<$>) is another name for fmap.)

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • Thanks! It was as simple as this. I think I was confused by the infix notation, but I thought something else was going on instead with `fmap` that was preventing it from working – Oh Fiveight Apr 12 '22 at 18:08