Haskell/XMonad: Каков естественный тип выражения того, что что-то должно быть сделано после последовательности действий?

У меня есть последовательность X() действия, во время которых определенные кнопки могут быть захвачены (и впоследствии не отпущены). Чтобы предотвратить захват кнопок, мне нужно убрать каждую кнопку в конце, например:

action1 >> action2 >> action3 >> ungrabAllButtons

Я хочу закодировать это требование как тип, чтобы action1, action2, action3 можно использовать только в том случае, если после этого кнопки не зафиксированы. То есть, хотя action1, action2действительно X() действия, я бы хотел, чтобы их нельзя было использовать как таковые, если они не обернуты в нечто вроде следующего (заимствование Python with ключевое слово):

withGrabbedButtons :: ??? -> X()
withGrabbedButtons action =
  action >> ungrabAllButtons  


-- correct ; complete_action does not leave the mouse grabbed
complete_action :: X()
complete_action = withGrabbedButtons (action1 >> action2 >> action3)

-- type error!
erroneous_single_action :: X()
erroneous_single_action = action1

-- type error!
erroneous_action :: X()
erroneous_action = action1 >> action2 >> action3

Это для того, чтобы люди случайно не пользовались action1, action2, action3 как X() действия, в то же время забывая разблокировать кнопки впоследствии.

Возможно ли это с системой типов Haskell? Заранее спасибо

2 ответа

Решение

То, что вы хотите сделать, это сделать упаковщик нового типа для X, с помощью GeneralizedNewtypeDeriving получить бесплатно Monad пример:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype XNeedsCleanup a = FromX { toX :: X a }
  deriving (Functor, Applicative, Monad)

Так как XNeedsCleanup это монада, вы можете связать несколько XNeedsCleanup вместе, как в вашем action1 >> action2 >> action3 пример; это свяжет все их завернутые X действия вместе внутри FromX обертка. Но вы не сможете связать полученное действие с X действие; вот где твой withGrabbedButtons приходит в:

withGrabbedButtons :: XNeedsCleanup () -> X ()
withGrabbedButtons action = toX action >> ungrabAllButtons

Если вы не экспортируете toX разверните, ваши клиенты не смогут использовать XNeedsCleanup ценность без прохождения withGrabbedButtons, Но если у них есть возможность использовать произвольную X действия, то, по-видимому, они могут импортировать все, что вы используете, чтобы определить ваши различные действия и могут переопределить их как "сырые" X действия. Так что просто для ясности: это не безопасность, ориентированная на безопасность, а просто предотвращение случайного забвения людей.

Возможно, вы захотите создать свою собственную версию брекета. Кронштейн работает в IO монада, но на основании быстрого взгляда на исходный код, я подозреваю, что вы могли бы сделать свою собственную версию, которая работает в X монада. Скобка гарантирует, что произойдет любое завершение (например, снятие всех кнопок), даже если возникнет исключение.

Другие вопросы по тегам