You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为XMobar编写可跨调用保留状态的监视器?

摘要

我的问题可以简化为一个是非题:“IO monad是否只能通过I/O操作保留状态?” 换句话说,我的理解是否正确:如果我需要编写一个被其他IO monad实例反复执行的操作

run :: IO String

那么除了将状态序列化到某处(例如文件或调用者提供的API)之外,无法让它在多次调用间保留状态?

如果答案是*“是的,无法直接实现”*,那么我的核心问题就是:如何为XMobar编写一个能保留本地状态的监视器?

详细背景

我正在试验xmobar,尤其是它的插件与监视器功能。

所有监视器都通过Run $ SomeMonitor运行,其中SomeMonitor必须是Runnable类型,即实现了(Exec r, Read r, Show r)接口的类型。其中Exec是关键接口,因为插件的业务逻辑需要写在这里,它的类型是IO String,可以通过IO操作生成XMobar显示的字符串。

我最初做了一个简单的实验:

data ArchUpdates = ArchUpdates deriving (Read, Show)

instance Exec ArchUpdates where
  rate _ = 36000
  run _ = fmap (makeMessage . length . lines) $ getCommandOutput "checkupdates"
  where
    makeMessage :: Int -> String
    makeMessage = show -- simplified version

这个插件用于查看我的ArchLinux系统中通过pacman可更新的包数量。

显然,XMobar显示的值会随时间变化,但这并非因为它显式依赖时间,而是因为系统状态发生了变化,这种变化通过I/O操作getCommandOutput "checkupdates"获取。

但如果我想开发一个显式依赖时间的插件呢?比如每秒更新一次,依次显示不同的字符串:先显示"A long time ago",然后是"in a galaxy far,",最后是"far away...",循环往复。

我的需求让我想到了State monad,但Exec约束强制我使用IO monad,所以我认为StateT转换器不是可行方案,因为我无法在run中使用其他包裹IO的monad。我需要IO包裹状态,但又无法修改IO!

因此,我能想到的唯一保留状态的方法是将其序列化到某处,然后重新读取:

instance Exec MyPlugin where
  rate _ = 1000
  run _ = do
    old <- getCurrentStateOfSelf
    return $ makeNewState old

其中makeNewState的类型是String -> String¹,但getCurrentStateOfSelf需要XMobar的API提供,否则就只能将状态写入文件?

run _ = do
    old <- readStateFromFile
    let new = makeNewState old
    writeStateToFile new
    return new

这种方法太糟糕了。


我尝试询问ChatGPT,它给出了以下代码:

import Control.Monad.State

func :: StateT Int IO String
func = do
  currentState <- get
  liftIO $ putStrLn $ "Current state: " ++ show currentState
  modify (+1)
  return "Hello, World!"

main :: IO ()
main = do
  (result, newState) <- runStateT func 0
  putStrLn $ "Result: " ++ result
  putStrLn $ "Final state: " ++ show newState

但我认为这不是解决方案,因为main需要负责多次调用runStateT并传递状态,而不是让func在多次调用间自动保留状态,这才是我想要的效果。

我猜测自己遇到了瓶颈:XMobar监视器基于IO monad设计,而我需要的是State monad(当然XMobar仍会使用IO monad在屏幕上显示内容,因为它本质是一个程序,编译自main函数,类型为IO ())。


(¹) 对于上述三个字符串的场景,它可以是一个带有[String]本地state的闭包,接收一个String,在cycle state中找到它,然后返回下一个元素。

内容的提问来源于stack exchange,提问作者Enlico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 05:40:34