假设我有这样的类型:
data Foo = Bar String | Baz | Qux String
我希望有这样的功能:
get : Foo -> String
get (Bar s) = s
get (Qux s) = s
正如所写,这是编译,但并不完全,因为有缺失的案例;换句话说,get Baz
被视为一个洞而不是一个没有进行类型检查的表达。
我想将Foo
的类型签名中的get
替换为指定值必须为Bar
或Qux
的内容。 如何表达Foo
类型的此子集?
答案 0 :(得分:4)
你也可以混合两种方法(由Kim Stiebel和Anton Trunov)并构建一个辅助数据类型。类型OnlyBarAndQux
只能使用Bar
和Qux
的值构建。对于idris,如果在调用get
时出现这种情况,则可以自动推断出证据:
module FooMe
data Foo = Bar String | Baz | Qux String
data OnlyBarAndQux: Foo -> Type where
BarEy: OnlyBarAndQux (Bar s)
QuxEx: OnlyBarAndQux (Qux s)
||| get a string from a Bar or Qux
total
get: (f: Foo) -> {auto prf : OnlyBarAndQux f} -> String
get (Bar s) {prf = BarEy} = s
get (Qux s) {prf = QuxEx} = s
-- Tests
test1: get $ Bar "hello" = "hello"
test1 = Refl
test2: get $ Qux "hello" = "hello"
test2 = Refl
-- does not compile
-- test3: get $ Baz = "hello"
答案 1 :(得分:3)
例如,我将遵循标准库中List head所采用的方法。这基本上是Markus wrote加上使用Dec
来证明Foo
不是Baz
是可判定的:
%default total
data Foo = Bar String | Baz | Qux String
data NotBaz : Foo -> Type where
IsBar: NotBaz(Bar z)
IsQux: NotBaz(Qux z)
Uninhabited (NotBaz Baz) where
uninhabited _ impossible
notBaz : (f : Foo) -> Dec (NotBaz f)
notBaz Baz = No absurd
notBaz (Bar s) = Yes IsBar
notBaz (Qux s) = Yes IsQux
get: (f : Foo) -> {auto ok : NotBaz f} -> String
get (Bar s) { ok = IsBar } = s
get (Qux s) { ok = IsQux } = s
s: String
s = get (Bar "bar")
对此有一些评论:
a -> Bool
来处理a
的子集类型;创建一个视图,如上面的NotBaz
。有关上下文,请参阅the Idris tutorial on views,this post和this answer。Dec
而不是相等。在任何情况下,您可以使用Dec
作为类型的谓词,您可以明确地决定谓词的真实性:请参阅上面的notBaz
。答案 2 :(得分:2)
有多种方法可以执行此操作,但最简单的方法可能是使Foo
成为一个类型构造函数,该构造函数接受一个参数,指示它是否Foo
String
在它或不在。在此示例中,我使用Bool
作为参数:
%default total
data Foo : Bool -> Type where
Bar : String -> Foo True -- a Foo constructed with Bar will have type Foo True
Baz : Foo False -- and a Foo constructed with Baz will have type Foo False
Qux : String -> Foo True
get : Foo True -> String
get (Bar s) = s
get (Qux s) = s
答案 3 :(得分:2)
我会选择金斯特贝尔的回答(如果改变Foo
是一个选项,正如@Eduardo Pareja Tobes所说的那样),但我想表现出另一种方式。您可以使用子集类型,这与依赖对相同:
total
get : (f ** Not (f = Baz)) -> String
get (f ** pf) with (f)
get (f ** _) | (Bar s) = s -- this is as before
get (f ** contra) | Baz = void $ contra Refl -- a contradictory case
get (f ** _) | (Qux s) = s -- this is as before
(f ** Not (f = Baz))
可以翻译为“某些f
类型Foo
,但不能翻译为Baz
”。
要致电get
,您需要提供类型为Foo
的元素的从属对,并且证明它不等于Baz
,如下所示:
s : String
s = get (Bar "bar" ** \Refl impossible)