异常和状态

Control.App 主要是为了更容易管理有异常和状态的应用程序的常见情况。我们可以抛出和捕捉错误列表中列出的异常( es 参数为 App ),并引入新的全局状态。

异常

List Error 是一个错误类型列表,可以使用以下函数抛出和捕获:

throw : HasErr err es => err -> App es a
catch : HasErr err es => App es a -> (err -> App es a) -> App es a

只要异常类型存在于错误列表 es 中,就可以使用 throwcatch 来抛出和捕获异常,如 HasErr 谓词所检查的那样(注意, ExceptionHasErr 的同义词):

data HasErr : Error -> List Error -> Type where
     Here : HasErr e (e :: es)
     There : HasErr e es -> HasErr e (e' :: es)

Exception : Error -> List Error -> Type
Exception = HasErr

最后,我们可以通过 handle 引入新的异常类型,运行可能会抛出异常的代码块,并处理任何异常:

handle : App (err :: e) a ->
         (onok : a -> App e b) ->
         (onerr : err -> App e b) -> App e b

添加状态

应用程序通常需要跟踪状态,我们在 App 中使用 Control.App 中定义的 State 类型支持这个原语:

data State : (tag : a) -> Type -> List Error -> Type

tag 只被用于区分不同的状态,在运行时是不需要,如用于访问和更新的 getput 类型:

get : (0 tag : _) -> State tag t e => App {l} e t
put : (0 tag : _) -> State tag t e => (1 val : t) -> App {l} e ()

它们使用 auto-implicit 来隐式传递带有相关 tagState ,因此我们仅通过标签来引用状态。在前面的 helloCount 中,我们使用了一个空类型 Counter 作为标签:

data Counter : Type where -- complete definition

错误列表 e 用来确保状态只在其被引入的错误列表中可用。状态是用 new 引入的:

new : t -> (1 p : State tag t e => App {l} e a) -> App {l} e a

请注意,这个类型告诉我们 new 用这个状态运行程序正好一次。然而,我们通常不直接使用 StateException ,而是使用接口来约束错误列表中允许的操作。在内部, State 是通过 IORef 实现的,这主要是出于性能的考虑。