xmonad `tags <- asks (workspaces .config)` 魔法——我该如何解析它?
xmonad `tags <- asks (workspaces . config)` magic -- how do I parse that?
我正在戳 xmonad-contrib
的 XMonad.Actions.WindowMenu
模块,试图使其可配置。
我很难理解以下内容:
在 original code 中的结构如下:
windowMenu :: X ()
windowMenu = withFocused $ \w -> do
tags <- asks (workspaces . config)
-- ...
了解那里发生的事情的最佳方式是什么?换句话说,整个 tags <- asks (workspaces . config)
是在什么情况下评估的?
我问的原因是当我尝试在不同的函数中重构它时:
defaultActs :: [(String, X ())]
defaultActs = do
tags <- asks (workspaces . config)
[ ("A: " ++ tag, return ()) | tag <- tags]
它因错误而爆炸:
• No instance for (MonadReader XConf [])
arising from a use of ‘asks’
• In a stmt of a 'do' block: tags <- asks (workspaces .
In the expression:
do tags <- asks (workspaces . config)
[("A: " ++ tag, return ()) | tag <- tags]
In an equation for ‘defaultActs’:
defaultActs
= do tags <- asks (workspaces . config)
[("A: " ++ tag, return ()) | tag <- tags]
编辑添加:
我理解该语句的类型:
ghci> :t asks (workspaces . config)
asks (workspaces . config) :: MonadReader XConf m => m [String]
ghci> :t withFocused
withFocused :: (Window -> X ()) -> X ()
但它破裂的原因(仍然)是个谜。
无需查看任何文档,我们就知道了这么多:这是在 withFocused
需要的任何类型的 do-notation 上下文中。它显然需要一个函数作为参数,并且该函数必须 return 某种类型的 monadic 值。此 do-notation 块的值必须具有相同的类型。它肯定 而不是 具有 [(String, X ())]
类型。 (嗯,好吧,它可以,因为 [a]
是一个 monad,但是这似乎不太可能是 withFocused
期望的结果类型)。
你可以通过查看 documentation:
来找出它的类型
withFocused :: (Window -> X ()) -> X ()
由于我们的 do-notation 位于第一个参数的 lambda 的 return 值内,因此它的类型必须为 X ()
。因此,
asks (workspaces . config) :: X t
对于一些 t
我们也可以通过查找 workspaces
的类型来知道。 <-
将那个 t
值绑定到名称 tags
,就像 do-notation 所做的那样。
引用评论:
Yes, windowMenu
has X ()
as a type, and my defaultActs
goes for a different type. What I don't understand is how does running the lambda through withFocused
grant it "access" to some extra context.
您可以使用 asks
以这种方式检索配置字段,因为 the X
monad 有一个 MonadReader XConf X
实例。 MonadReader
通常用于提供这种配置访问权限。
The reason I'm asking is that when I try to refactor this in a different function:
defaultActs :: [(String, X ())]
defaultActs = do
tags <- asks (workspaces . config)
[ ("A: " ++ tag, return ()) | tag <- tags]
查看您的定义,我怀疑您打算 defaultActs
成为工作区标签的修改列表。既然如此,你想要的大概是这样的:
defaultActs :: X [String]
defaultActs = do
tags <- asks (workspaces . config)
return [ "A: " ++ tag | tag <- tags]
也就是说,从配置中获取标签,生成修改后的列表,然后 return 它在 X
monad 的上下文中。
好的,多亏了我才弄明白:
defaultActions :: XConf -> [(String, X ())]
defaultActions = do
tags <- asks (workspaces . config)
return ([ ("Cancel menu", return ())
, ("Close" , kill)
, ("Maximize" , withFocused $ \w -> sendMessage $ maximizeRestore w)
, ("Minimize" , withFocused $ \w -> minimizeWindow w)
] ++
[ ("Move to " ++ tag, windows $ W.shift tag) | tag <- tags ])
windowMenu' :: (XConf -> [(String, X ())]) -> X ()
windowMenu' actions = withFocused $ \w -> do
acts <- asks actions
Rectangle x y wh ht <- getSize w
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
runSelectedAction gsConfig acts
-- now it composes well, and I can pass in my own `actions` to `windowMenu`
windowMenu = windowMenu' defaultActions
顺便说一句,我无法让它作为 X [String]
类型工作(不知道为什么),但上面的解决方案似乎工作得很好。也许严格来说它不是最好的(不确定),但它带我去我想去的地方。
最简单的事情是让 windowMenu
和 defaultActions
在 X
monad 中运行,它已经有适当的 MonadReader
实例。所以:
defaultActions :: X [(String, X ())]
defaultActions = do
tags <- asks (workspaces . config)
return ([ ("Cancel menu", return ())
, ("Close" , kill)
, ("Maximize" , withFocused $ \w -> sendMessage $ maximizeRestore w)
, ("Minimize" , withFocused $ \w -> minimizeWindow w)
] ++
[ ("Move to " ++ tag, windows $ W.shift tag) | tag <- tags ])
windowMenu :: X ()
windowMenu = withFocused $ \w -> do
acts <- defaultActions
Rectangle x y wh ht <- getSize w
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
runSelectedAction gsConfig acts
不需要 中使 windowMenu'
成为参数化高阶函数的恶作剧。如果你真的想制作一个 windowMenu'
让你对动作列表进行参数化,直接这样做:
windowMenu' :: [(String, X ())] -> X ()
windowMenu' acts = withFocused $ \w -> do
Rectangle x y wh ht <- getSize w
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
runSelectedAction gsConfig acts
在这样的世界中,您可以 运行 defaultActions
并将其结果传递给 windowMenu'
使用 (>>=)
(或更多 do
表示法):
windowMenu :: X ()
windowMenu = defaultActions >>= windowMenu'
-- OR
windowMenu = do
acts <- defaultActions
windowMenu' acts
我正在戳 xmonad-contrib
的 XMonad.Actions.WindowMenu
模块,试图使其可配置。
我很难理解以下内容:
在 original code 中的结构如下:
windowMenu :: X ()
windowMenu = withFocused $ \w -> do
tags <- asks (workspaces . config)
-- ...
了解那里发生的事情的最佳方式是什么?换句话说,整个 tags <- asks (workspaces . config)
是在什么情况下评估的?
我问的原因是当我尝试在不同的函数中重构它时:
defaultActs :: [(String, X ())]
defaultActs = do
tags <- asks (workspaces . config)
[ ("A: " ++ tag, return ()) | tag <- tags]
它因错误而爆炸:
• No instance for (MonadReader XConf [])
arising from a use of ‘asks’
• In a stmt of a 'do' block: tags <- asks (workspaces .
In the expression:
do tags <- asks (workspaces . config)
[("A: " ++ tag, return ()) | tag <- tags]
In an equation for ‘defaultActs’:
defaultActs
= do tags <- asks (workspaces . config)
[("A: " ++ tag, return ()) | tag <- tags]
编辑添加:
我理解该语句的类型:
ghci> :t asks (workspaces . config)
asks (workspaces . config) :: MonadReader XConf m => m [String]
ghci> :t withFocused
withFocused :: (Window -> X ()) -> X ()
但它破裂的原因(仍然)是个谜。
无需查看任何文档,我们就知道了这么多:这是在 withFocused
需要的任何类型的 do-notation 上下文中。它显然需要一个函数作为参数,并且该函数必须 return 某种类型的 monadic 值。此 do-notation 块的值必须具有相同的类型。它肯定 而不是 具有 [(String, X ())]
类型。 (嗯,好吧,它可以,因为 [a]
是一个 monad,但是这似乎不太可能是 withFocused
期望的结果类型)。
你可以通过查看 documentation:
来找出它的类型withFocused :: (Window -> X ()) -> X ()
由于我们的 do-notation 位于第一个参数的 lambda 的 return 值内,因此它的类型必须为 X ()
。因此,
asks (workspaces . config) :: X t
对于一些 t
我们也可以通过查找 workspaces
的类型来知道。 <-
将那个 t
值绑定到名称 tags
,就像 do-notation 所做的那样。
引用评论:
Yes,
windowMenu
hasX ()
as a type, and mydefaultActs
goes for a different type. What I don't understand is how does running the lambda throughwithFocused
grant it "access" to some extra context.
您可以使用 asks
以这种方式检索配置字段,因为 the X
monad 有一个 MonadReader XConf X
实例。 MonadReader
通常用于提供这种配置访问权限。
The reason I'm asking is that when I try to refactor this in a different function:
defaultActs :: [(String, X ())] defaultActs = do tags <- asks (workspaces . config) [ ("A: " ++ tag, return ()) | tag <- tags]
查看您的定义,我怀疑您打算 defaultActs
成为工作区标签的修改列表。既然如此,你想要的大概是这样的:
defaultActs :: X [String]
defaultActs = do
tags <- asks (workspaces . config)
return [ "A: " ++ tag | tag <- tags]
也就是说,从配置中获取标签,生成修改后的列表,然后 return 它在 X
monad 的上下文中。
好的,多亏了
defaultActions :: XConf -> [(String, X ())]
defaultActions = do
tags <- asks (workspaces . config)
return ([ ("Cancel menu", return ())
, ("Close" , kill)
, ("Maximize" , withFocused $ \w -> sendMessage $ maximizeRestore w)
, ("Minimize" , withFocused $ \w -> minimizeWindow w)
] ++
[ ("Move to " ++ tag, windows $ W.shift tag) | tag <- tags ])
windowMenu' :: (XConf -> [(String, X ())]) -> X ()
windowMenu' actions = withFocused $ \w -> do
acts <- asks actions
Rectangle x y wh ht <- getSize w
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
runSelectedAction gsConfig acts
-- now it composes well, and I can pass in my own `actions` to `windowMenu`
windowMenu = windowMenu' defaultActions
顺便说一句,我无法让它作为 X [String]
类型工作(不知道为什么),但上面的解决方案似乎工作得很好。也许严格来说它不是最好的(不确定),但它带我去我想去的地方。
最简单的事情是让 windowMenu
和 defaultActions
在 X
monad 中运行,它已经有适当的 MonadReader
实例。所以:
defaultActions :: X [(String, X ())]
defaultActions = do
tags <- asks (workspaces . config)
return ([ ("Cancel menu", return ())
, ("Close" , kill)
, ("Maximize" , withFocused $ \w -> sendMessage $ maximizeRestore w)
, ("Minimize" , withFocused $ \w -> minimizeWindow w)
] ++
[ ("Move to " ++ tag, windows $ W.shift tag) | tag <- tags ])
windowMenu :: X ()
windowMenu = withFocused $ \w -> do
acts <- defaultActions
Rectangle x y wh ht <- getSize w
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
runSelectedAction gsConfig acts
不需要 windowMenu'
成为参数化高阶函数的恶作剧。如果你真的想制作一个 windowMenu'
让你对动作列表进行参数化,直接这样做:
windowMenu' :: [(String, X ())] -> X ()
windowMenu' acts = withFocused $ \w -> do
Rectangle x y wh ht <- getSize w
Rectangle sx sy swh sht <- gets $ screenRect . W.screenDetail . W.current . windowset
let originFractX = (fi x - fi sx + fi wh / 2) / fi swh
originFractY = (fi y - fi sy + fi ht / 2) / fi sht
gsConfig = (buildDefaultGSConfig colorizer)
{ gs_originFractX = originFractX
, gs_originFractY = originFractY }
runSelectedAction gsConfig acts
在这样的世界中,您可以 运行 defaultActions
并将其结果传递给 windowMenu'
使用 (>>=)
(或更多 do
表示法):
windowMenu :: X ()
windowMenu = defaultActions >>= windowMenu'
-- OR
windowMenu = do
acts <- defaultActions
windowMenu' acts