xmonad `tags <- спрашивает (workspaces. config)` magi c - как мне это разобрать? - PullRequest
1 голос
/ 03 апреля 2020

Я высовываю модуль XMonad.Actions.WindowMenu из xmonad-contrib, пытаюсь сделать его настраиваемым.

И мне трудно понять следующий бит:

В Исходный код представляет собой такую ​​конструкцию, как:

windowMenu :: X ()
windowMenu = withFocused $ \w -> do
    tags <- asks (workspaces . config)
    -- ...

Как лучше всего go понять, что там происходит? Другими словами, в каком контексте оценивается целое tags <- asks (workspaces . config)?

Причина, по которой я спрашиваю, заключается в том, что, когда я пытаюсь реорганизовать это в другую функцию:

defaultActs :: [(String, X ())]
defaultActs = do
    tags <- asks (workspaces . config)
    [ ("A: " ++ tag, return ()) | tag <- tags]

it взрывается с ошибкой:

    • 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 ()

, но причина, по которой он ломается (все еще ) загадка.

Ответы [ 4 ]

2 голосов
/ 03 апреля 2020

Самое простое - просто заставить windowMenu и defaultActions работать в монаде X, которая уже имеет соответствующий экземпляр 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
1 голос
/ 03 апреля 2020

Цитирование комментария:

Да, windowMenu имеет X () в качестве типа, а мой defaultActs относится к другому типу. Что я не понимаю, так это то, как запуск лямбды через withFocused предоставляет ей "доступ" к некоторому дополнительному контексту.

Вы можете использовать asks для извлечения полей конфигурации таким образом, потому что там MonadReader XConf X экземпляр для монады X . MonadReader обычно используется для предоставления такого вида доступа к конфигурации.


Причина, по которой я спрашиваю, заключается в том, что, когда я пытаюсь реорганизовать это в другую функцию:

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]

То есть получить теги из конфигурации, создать модифицированный список и вернуть его в контексте монады X.

1 голос
/ 03 апреля 2020

Хорошо, благодаря ответу duplode Я смог понять это:

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] (не уверен почему), но решение выше, кажется, работает достаточно хорошо. Может быть, это не совсем лучший (не уверен в этом), но он уводит меня туда, куда я хочу go.

0 голосов
/ 03 апреля 2020

Не глядя на какую-либо документацию, мы знаем это очень много: это в контексте do-notation для любого типа, необходимого withFocused. Очевидно, он принимает функцию в качестве аргумента, и эта функция должна возвращать монадическое c значение некоторого типа. Значение этого блока do-notation должно иметь тот же тип. Конечно, не имеет тип [(String, X ())]. (Ну, ладно, могло бы, так как [a] является монадой, но кажется маловероятным, что этот тип withFocused будет ожидать в результате).

Вы можете узнать, какой тип он имеет просматривая документацию :

withFocused :: (Window -> X ()) -> X ()

Поскольку наша нотация находится внутри возвращаемого значения лямбды для первого параметра, она должна иметь тип X (). Следовательно,

asks (workspaces . config) :: X t

для некоторого t, который мы также можем узнать, посмотрев тип workspaces. <- связывает это значение t с именем tags обычным образом, как это делает нотация.

...