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
монада. Скобка гарантирует, что произойдет любое завершение (например, снятие всех кнопок), даже если возникнет исключение.