Xmonad技术问询:如何实现窗口属性与字符串列表的对比(而非仅单个字符串)
这个问题我之前也碰到过!核心是className =?只能接受单个字符串参数,而你传了一个字符串列表,导致类型不匹配。下面给你几个可行的解决方案,从简单到复用性强的都有:
1. 用逻辑或组合多个单个匹配(适合少量值)
直接把每个类名的判断用<||>(Query类型的逻辑或操作符)组合起来,代码直观易懂,适合要匹配的类名不多的场景:
, ((modMask, xK_e), bindFirst [ ((className =? "st") <||> (className =? "xterm"), spawn "termite") , (pure True, kill) ])
<||>会把两个Query Bool合并成一个新的Query Bool,只要其中任意一个条件为真,整个判断就会通过。
2. 用fmap结合elem(适合较长的列表)
如果要匹配的类名比较多,写一堆<||>太繁琐,可以利用Haskell的Functor特性,用fmap把elem函数应用到className的查询结果上:
, ((modMask, xK_e), bindFirst [ (fmap (`elem` ["st", "xterm"]) className, spawn "termite") , (pure True, kill) ])
这里的关键是:className是Query String类型,fmap可以把普通的String -> Bool函数(也就是\c -> c elem ["st", "xterm"])转换成Query Bool类型,正好符合bindFirst要求的条件类型。
3. 封装成辅助函数(方便复用)
如果你经常需要匹配多个类名,可以自己封装一个小函数,把这个逻辑抽离出来,让代码更简洁:
-- 把这个函数放在配置文件的合适位置(比如开头的自定义函数区) classNamesIn :: [String] -> Query Bool classNamesIn names = fmap (`elem` names) className -- 然后在按键绑定中直接调用 , ((modMask, xK_e), bindFirst [ (classNamesIn ["st", "xterm"], spawn "termite") , (pure True, kill) ])
以后不管要匹配多少个类名,只要传入字符串列表给classNamesIn就行,复用性拉满。
补充:你之前的错误原因
=?的类型是Query String -> String -> Query Bool,它要求第二个参数必须是单个字符串,但你传入的["st", "xterm"]是字符串列表(类型为[String]),所以Haskell编译器无法匹配类型,才会抛出Couldn't match type ‘[Char]’ with ‘Char’的错误。
而你尝试用elem时,它返回的是普通的Bool值,但bindFirst需要的是Query Bool类型(带有Xmonad上下文的布尔值),所以用fmap把elem的逻辑提升到Query上下文里,就能解决这个问题了。
内容的提问来源于stack exchange,提问作者lwndt

