Haskell/XMonad: What is the natural type for expressing that something must be done after a sequence of actions?

元气小坏坏 提交于 2019-12-24 07:28:40

问题


I have a sequence of X() actions during which certain buttons might be grabbed (and not released afterwards). In order to prevent buttons from ending up grabbed, I therefore have to ungrab every button at the end, for example:

action1 >> action2 >> action3 >> ungrabAllButtons

I wish to encode this requirement as a type, so that action1, action2, action3 can only be used if the buttons are ungrabbed afterwards. That is, even though action1, action2, are really X() actions, I would like them not to be usable as such unless they are wrapped in something like the following (borrowing Python's with keyword):

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

This is so that people do not accidentally use action1, action2, action3 as X() actions, while forgetting to ungrab the buttons afterwards.

Is this possible with Haskell's type system? Thanks beforehand.


回答1:


What you will want to do is make a newtype wrapper for X, using GeneralizedNewtypeDeriving to get a free Monad instance:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

Because XNeedsCleanup is a monad, you can bind multiple XNeedsCleanup together, as in your action1 >> action2 >> action3 example; this will bind all of their wrapped X actions together inside the FromX wrapper. But you won't be able to bind the resulting action with an X action; that's where your withGrabbedButtons comes in:

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

If you don't export the toX unwrapper, your clients won't be able to use an XNeedsCleanup value without going through withGrabbedButtons. But if they have the ability to use arbitrary X actions, then presumably they can import whatever you use to define your various actions and could reimplement them as "raw" X actions. So just to be explicit: this isn't security-oriented safety, just preventing people from accidentally forgetting the cleanup.




回答2:


You might want to create your own version of bracket. Bracket works in the IO monad, but based on a quick peek at the source code, I suspect you could make your own version that runs in the X monad. Bracket will ensure that any finalisation (e.g. ungrabbing all buttons) occurs, even if an exception is raised.



来源:https://stackoverflow.com/questions/30106827/haskell-xmonad-what-is-the-natural-type-for-expressing-that-something-must-be-d

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!