Haskell / XMonad:表达必须在一系列动作之后完成某事的自然类型是什么?

时间:2015-05-07 16:31:27

标签: haskell types with-statement xmonad

我有一系列X()个动作,在此期间可能会抓取某些按钮(之后不会释放)。为了防止按钮最终被抓住,我因此必须在最后取消每个按钮,例如:

action1 >> action2 >> action3 >> ungrabAllButtons

我希望将此要求编码为一种类型,以便action1action2action3只能在以后按钮未被删除时使用。也就是说,即使action1action2实际上是X()个动作,我希望它们不会被这样使用,除非它们包含在以下内容中(借用Python&#39) ; s 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

这样人们就不会意外地使用action1action2action3作为X()操作,而忘记在之后取消按钮。

这是否适用于Haskell的类型系统?先谢谢。

2 个答案:

答案 0 :(得分:1)

您要做的是为X创建一个newtype包装器,使用GeneralizedNewtypeDeriving获取免费Monad实例:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

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

由于XNeedsCleanup是一个monad,您可以将多个XNeedsCleanup绑定在一起,就像在action1 >> action2 >> action3示例中一样;这将在X包装器中将所有包装的FromX操作绑定在一起。但是,您将无法使用X操作绑定结果操作;那就是你withGrabbedButtons进来的地方:

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

如果您不导出toX解包器,则客户将无法通过XNeedsCleanup使用withGrabbedButtons值。但如果他们有能力使用任意X个动作,那么可能他们可以导入您用来定义各种动作的任何动作,并可以将它们重新实现为“原始”X动作。所以要明确一点:这不是安全导向的安全,只是防止人们意外忘记清理。

答案 1 :(得分:0)

您可能想要创建自己的bracket版本。 Bracket在package fourfive; import java.util.Random; public class Computer extends Human { protected static Random rand = new Random(); protected int maxGuess; Computer(int playerNum) { super(playerNum); maxGuess = 1000; } Computer(int playerNum, int topNum){ super(playerNum); maxGuess = topNum; } @Override public void guess() { int guess = rand.nextInt(maxGuess); System.out.println("Bot " + playerNum + " turn *" + guess + "*"); guesses.push(guess); } public int getMaxGuess() { return maxGuess; } public void setMaxGuess(int num) { maxGuess = num; } } monad中工作,但基于对源代码的快速浏览,我怀疑你可以创建自己的版本在IO monad中运行。即使引发异常,Bracket也会确保发生任何终结(例如,取消所有按钮)。