许多库都有一个接口,在该接口上,必须首先调用一个函数来初始化该库,该接口将返回一些值(通常是一个指针,有时称为“上下文”或“句柄”)以传递给其他函数,例如(在C):
Library *initialize(long flags);
A mkA(Library *library, long n);
B mkB(Library *library, A a);
如果未使用相同的上下文mkB
创建a
,则调用library
是错误的。
我想在Agda(通过Haskell)中绑定API,并在类型系统中强制执行相同上下文的要求,但不确定如何安全地这样做。 (请注意,API是纯净的,例如,mkA
每次使用相同的参数调用时都会返回一个等效值。)我对API的想法如下:
module Data.Tagged where
open import Level
data Tagged {ℓ ℓ′} {A : Set ℓ} (a : A) (B : Set ℓ′) : Set (ℓ ⊔ ℓ′) where
tag : B → Tagged a B
module Raw where
postulate
Library A B HsWord : Set
initialize : IO Library
mkA : Library → HsWord → A
mkB : Library → A → B
{-# COMPILE GHC Library = type Library #-}
{-# COMPILE GHC A = type A #-}
{-# COMPILE GHC B = type B #-}
{-# COMPILE GHC HsWord = type Word #-}
open import Data.Tagged
open Raw public using (Library)
module _ {lib : Library} where
A B : Set
A = Tagged lib A
B = Tagged lib B
mkA : Raw.HsWord → A
mkA n = tag (Raw.mkA lib n)
mkB : A → B
mkB a = tag (Raw.mkB lib a)
因此将确保类型安全-mkB
仅接受使用相同A
进行的Library
值-除非可以写以下术语:
castTagged : ∀ {a a′ B} → Tagged a B → Tagged a′ B
castTagged (tag b) = tag b
我会定义
A = Tagged lib A
B = Tagged lib B
抽象,除非它们即使在同一模块中的其他类型签名中也变得不可约。我希望能够将它们定义为在同一模块中可还原,而在其他模块中不可还原,但不确定是否可行。我想将Data.Tagged
保留在一个单独的模块中,以便在那里证明其他定理,而不是为我要绑定的每个库重新定义它。
无论如何,如何在Agda中定义这样一种标签安全的API?