编译器能否在Idris中隐式找到证据?

时间:2018-02-19 21:57:48

标签: idris dependent-type

我正在尝试学习伊德里斯并且依赖于类型编程。

我正在努力的是以下想法。假设我创建了这种类型:

data Entry: String -> a -> Type where
  E: (name: String) -> (value: a) -> Entry name a

data Rec: List (String, Type) -> Type where
  Nil: Rec []
  Cons: Entry s a -> Rec ts -> Rec ((s, a) :: ts)

myRec: Rec [("name", String), ("age", Integer)]
myRec = Cons (E "name" "Rafael") (Cons (E "age" 35) Nil)

这是一种字典,其字段由类型为String的键标识,并且我在此[(String, Type)]列表中存储与给定键关联的字段的类型。

我想编写一个函数,给定key: String我可以以某种方式证明列表中的值,我可以检索存储在该键下的值。

我的尝试是这样的:

data Contains : String -> Type -> List (String, Type) -> Type where
  H : Contains s a ((s, a) :: ts)
  T : Contains s a xs -> Contains s a (x :: xs)

getFieldType : {a: Type}
            -> (f: String)
            -> (fs: List (String, Type))
            -> (Contains f a fs)
            -> Type
getFieldType f ((f, a) :: xs) H = a
getFieldType f (_ :: xs) (T y) = getFieldType f xs y

get : (f: String) -> Rec fs -> getFieldType f fs p
get {p = H} f (Cons (E f value) y) = value
get {p = (T z)} f (Cons _ y) = get {p=z} f y

现在,我的问题。

我可以通过提供get类型的充分证据轻松使用此函数Contains f a fs

*src/test> get {p=H} "name" myRec
"Rafael" : String


*src/test> get {p=T H} "age" myRec
35 : Integer

但看起来可以自动找到这些证据。可以自动化吗?因为对于程序员来说,使用哪些证据是显而易见的,因此编译器看起来很聪明,可以找到它。这可能吗?

编辑:

我开始意识到,如果记录中有两个具有相同名称的字段,这将会在我的脸上爆炸......也许[(String, Type)]列表不是最好的抽象使用。< / p>

1 个答案:

答案 0 :(得分:3)

Idris可以使用auto搜索校样,请参阅the docs。在您的情况下,您只需要将get的类型更改为

get : (f : String) -> Rec fs -> {auto p : Contains f a fs} -> getFieldType f fs p

然后,Idris在调用get时尝试在编译时构建证明,但如果需要,您仍然可以明确get {p=T H}

另一方面,如果您定义Nil(::)(而不是Cons),Idris支持列表的语法糖。所以你可以美化一些事情:

data Rec: List (String, Type) -> Type where
  Nil : Rec []
  (::) : Entry s a -> Rec ts -> Rec ((s, a) :: ts)

data Contains : String -> Type -> List (String, Type) -> Type where
  H : Contains s a ((s, a) :: ts)
  T : Contains s a xs -> Contains s a (x :: xs)

myRec: Rec [("name", String), ("age", Integer)] 
myRec = [E "name" "Rafael", E "age" 35]

根据您的使用情况,您还可以将Contains s a fs简化为Contains s fs

  

我开始意识到,如果记录中有两个字段   这个名字会在我脸上爆炸......也许是   [(String,Type)] list不是最好的抽象使用。

如果您想保证Rec中只有一个密钥,可以使用以下内容:

data Contains : String -> Type -> List (String, Type) -> Type where
  H : Contains s a ((s, a) :: ts)
  T : Contains s a xs -> Contains s a (x :: xs)

data Rec: List (String, Type) -> Type where
  Nil : Rec []
  (::) : Entry s a -> Rec ts -> {p : Not (Contains s a ts)} -> Rec ((s, a) :: ts)

但是你可能需要一个单独的构造函数,因为Idris并不擅长证明Not t。 : - )

如果您想要练习,可以尝试将类似的工作Rec实现为:

data Rec : Vect k String -> Vect k Type -> Type where
  Empty : Rec [] []
  Add : {ty : Type} -> (k : String) -> (v : ty) -> Rec ks tys -> Rec (k :: ks) (ty :: tys)