我可以在 XMobar 中有一个监视器来将一次调用的状态保持到下一次调用吗?

问题描述 投票:0回答:1

tl;博士

我想我的问题可以归结为是/否问题:

IO
单子只能通过I/O操作保持状态吗?”换句话说,如果我必须编写一个操作,我的理解是否正确

run :: IO String

由其他一些

IO
monad 实例重复执行,我无法编写它来保留状态的内存,从调用到下一个状态,而不是通过在某处序列化状态(例如,到文件或在调用者中(如果它提供 API 来执行此操作)?

但是如果答案是“是的,你不能这样做”,那么问题就是标题中的问题:如何为 XMobar 编写一个保持本地状态的监视器?

完整故事

我正在尝试使用 xmobar,特别是它的 插件和监视器

所有监视器都通过

Run $ SomeMonitor
运行,其中
SomeMonitor
必须是
Runnable
类型,即它可以是实现
(Exec r, Read r, Show r)
接口的任何类型,其中
Exec
是有趣的,因为它是你的地方放置插件的业务逻辑,因为它具有类型
IO String
,因此它可以使用
String
创建XMobar显示的
IO

这是我的第一个非常简单的实验,

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
,但是
Exec
约束迫使我使用
IO
单子,所以我不认为
StateT
变压器是可行的方法,因为我可以不要在 IO 中使用
another
monad 包装
run
。我宁愿需要
IO
来包装一些状态...但我无法更改
IO

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

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

其中

makeNewState
String -> String
1,但是
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
进行多次调用,将状态从一个调用传输到另一个调用;这不像多次调用 to
main
正在访问
func
的连续状态,这正是我想要的。

我想答案可能是我碰壁了:XMobar 监视器是在

IO
monad 中设计的,而我需要
State
monad(那么显然 XMobar 仍然会使用
IO
monad 来显示屏幕上的东西,因为毕竟是一个程序,所以它来自于编译一个
main
函数,也就是
IO ()
)。


(1) 对于上面 3 个不同的字符串,它可能是一个带有

[String]
本地
state
的闭包,它接受
String
,将其定位在
cycle state
中,然后返回该项目.

haskell functional-programming state-monad io-monad xmobar
1个回答
0
投票

这有点取决于。您向 xmobar 提供这些 IO 操作的 API 是什么?如果您可以执行自己的 IO 来创建 IO 操作,则可以构造一个位于 IO 操作本身之外的 IORef,以便它引用和存储数据。但是,如果您无法与 xmobar 的启动方式进行交互,它要求你做的只是一些

IO a
操作,你不能真正将外部 IORef 偷偷带进去(除非你用
unsafePerformIO
作弊。

但是让我们想象一下您自己正在运行 xmobar,并且它返回某种 IO 操作:

runXmobar :: [IO String] -> IO ()
runXmobar = undefined -- implemented by xmobar

您的

main
可能看起来像这样:

main = do
  ref <- newIORef 0
  runXmobar [uptick ref]

uptick :: IORef Int -> IO String
uptick r = print =<< atomicModifyIORef' next r
  where next x = (x + 1, show x)
© www.soinside.com 2019 - 2024. All rights reserved.