在 Idris 中列出总和类型元素的惯用方式
Idiomatic way of listing elements of a sum type in Idris
我有一个表示算术运算符的求和类型:
data Operator = Add | Substract | Multiply | Divide
我正在尝试为它编写一个解析器。为此,我需要所有运算符的详尽列表。
在 Haskell 中,我会像以下 Whosebug 问题中建议的那样使用 deriving (Enum, Bounded)
:Getting a list of all possible data type values in Haskell
不幸的是,Idris 中似乎没有 Issue #19. There is some ongoing work by David Christiansen on the question so hopefully the situation will improve in the future : david-christiansen/derive-all-the-instances
建议的这种机制
来自 Scala,我习惯于手动列出元素,所以我很自然地想到了以下内容:
Operators : Vect 4 Operator
Operators = [Add, Substract, Multiply, Divide]
为了确保Operators
包含所有元素,我添加了以下证明:
total
opInOps : Elem op Operators
opInOps {op = Add} = Here
opInOps {op = Substract} = There Here
opInOps {op = Multiply} = There (There Here)
opInOps {op = Divide} = There (There (There Here))
因此,如果我将一个元素添加到 Operator
而未将其添加到 Operators
,完整性检查器会抱怨:
Parsers.opInOps is not total as there are missing cases
它完成了工作,但它有很多样板。
我错过了什么?有更好的方法吗?
可以选择使用 elaborator reflection 等语言的特性来获取所有构造函数的列表。
这是解决这个特定问题的一个非常愚蠢的方法(我发布这个是因为目前的文档非常稀缺):
%language ElabReflection
data Operator = Add | Subtract | Multiply | Divide
constrsOfOperator : Elab ()
constrsOfOperator =
do (MkDatatype _ _ _ constrs) <- lookupDatatypeExact `{Operator}
loop $ map fst constrs
where loop : List TTName -> Elab ()
loop [] =
do fill `([] : List Operator); solve
loop (c :: cs) =
do [x, xs] <- apply `(List.(::) : Operator -> List Operator -> List Operator) [False, False]
solve
focus x; fill (Var c); solve
focus xs
loop cs
allOperators : List Operator
allOperators = %runElab constrsOfOperator
一些评论:
- 似乎要解决具有相似结构的任何归纳数据类型的这个问题,需要完成 Elaborator Reflection: Extending Idris in Idris 论文。
- 也许 pruviloj 库有一些东西可以更容易地解决更一般情况下的这个问题。
我有一个表示算术运算符的求和类型:
data Operator = Add | Substract | Multiply | Divide
我正在尝试为它编写一个解析器。为此,我需要所有运算符的详尽列表。
在 Haskell 中,我会像以下 Whosebug 问题中建议的那样使用 deriving (Enum, Bounded)
:Getting a list of all possible data type values in Haskell
不幸的是,Idris 中似乎没有 Issue #19. There is some ongoing work by David Christiansen on the question so hopefully the situation will improve in the future : david-christiansen/derive-all-the-instances
建议的这种机制来自 Scala,我习惯于手动列出元素,所以我很自然地想到了以下内容:
Operators : Vect 4 Operator
Operators = [Add, Substract, Multiply, Divide]
为了确保Operators
包含所有元素,我添加了以下证明:
total
opInOps : Elem op Operators
opInOps {op = Add} = Here
opInOps {op = Substract} = There Here
opInOps {op = Multiply} = There (There Here)
opInOps {op = Divide} = There (There (There Here))
因此,如果我将一个元素添加到 Operator
而未将其添加到 Operators
,完整性检查器会抱怨:
Parsers.opInOps is not total as there are missing cases
它完成了工作,但它有很多样板。 我错过了什么?有更好的方法吗?
可以选择使用 elaborator reflection 等语言的特性来获取所有构造函数的列表。
这是解决这个特定问题的一个非常愚蠢的方法(我发布这个是因为目前的文档非常稀缺):
%language ElabReflection
data Operator = Add | Subtract | Multiply | Divide
constrsOfOperator : Elab ()
constrsOfOperator =
do (MkDatatype _ _ _ constrs) <- lookupDatatypeExact `{Operator}
loop $ map fst constrs
where loop : List TTName -> Elab ()
loop [] =
do fill `([] : List Operator); solve
loop (c :: cs) =
do [x, xs] <- apply `(List.(::) : Operator -> List Operator -> List Operator) [False, False]
solve
focus x; fill (Var c); solve
focus xs
loop cs
allOperators : List Operator
allOperators = %runElab constrsOfOperator
一些评论:
- 似乎要解决具有相似结构的任何归纳数据类型的这个问题,需要完成 Elaborator Reflection: Extending Idris in Idris 论文。
- 也许 pruviloj 库有一些东西可以更容易地解决更一般情况下的这个问题。