Haskell 是否存在具有可约束类型的语言?
有没有一种类型化编程语言可以像下面两个例子那样约束类型Haskell 是否存在具有可约束类型的语言?,haskell,agda,dependent-type,Haskell,Agda,Dependent Type,有没有一种类型化编程语言可以像下面两个例子那样约束类型 概率是一个浮点数,最小值为0.0,最大值为1.0 type Probability subtype of float where max_value = 0.0 min_value = 1.0 离散概率分布是一个映射,其中:键应该都是相同的类型,值都是概率,值之和=1.0 type DPD<K> subtype of map<K, Probability> where sum(values
type Probability subtype of float
where
max_value = 0.0
min_value = 1.0
type DPD<K> subtype of map<K, Probability>
where
sum(values) = 1.0
类型DPD映射的子类型
哪里
总和(值)=1.0
概率
:
第264行定义了具有求和条件的概率质量函数类型
与Agda相比,有些语言具有更直接的细化类型,例如,Nimrod是一种支持此概念的新语言。它们被称为子范围。这里有一个例子。你可以在这里学习更多关于这门语言的知识
您可以在Haskell中执行此操作,并使用它扩展Haskell。谓词在编译时由SMT解算器管理,这意味着证明是完全自动的,但您可以使用的逻辑受到SMT解算器处理内容的限制。(令人高兴的是,现代SMT解决方案相当通用!) 一个问题是,我认为Liquid Haskell目前不支持浮动。如果没有,应该可以纠正,因为SMT解算器有浮点数理论。您还可以假装浮点数实际上是有理数(或者甚至在Haskell中使用
rational
)。考虑到这一点,您的第一种类型可以如下所示:
{p : Float | p >= 0 && p <= 1}
measure total :: [(a, Float)] -> Float
total [] = 0
total ((_, p):ps) = p + probDist ps
(您可能还需要将[]
包装在新类型中。)
现在,您可以在细化中使用total
来约束列表:
{dist: [(a, Float)] | total dist == 1}
Liquid Haskell的巧妙之处在于,所有的推理都是在编译时自动进行的,作为使用某种受限逻辑的回报。(像total
这样的度量在如何编写它们方面也非常有限,这是Haskell的一个小子集,有“每个构造函数正好一个案例”这样的规则)。这意味着这种风格的精化类型的功能不如依赖类型的精化类型强大,但更易于使用,这使得它们更实用。Perl6有一个“类型子集”,可添加任意条件以创建“子类型”
对于你的具体问题:
subset Probability of Real where 0 .. 1;
及
(注意:在当前的实现中,“where”部分是在运行时检查的,但由于“real type”是在编译时检查的(包括您的类),并且std中有纯注释(是纯的
)(主要是perl6)(这些注释也在像*
等操作符上),这只是一个努力的问题(不应该更多)
更一般地说:
# (%% is the "divisible by", which we can negate, becoming "!%%")
subset Even of Int where * %% 2; # * creates a closure around its expression
subset Odd of Int where -> $n { $n !%% 2 } # using a real "closure" ("pointy block")
然后,您可以检查数字是否与智能匹配运算符匹配~
:
say 4 ~~ Even; # True
say 4 ~~ Odd; # False
say 5 ~~ Odd; # True
而且,多亏了multi-sub
s(或者多个,实际上是多个方法或其他),我们可以基于此进行调度:
multi say-parity(Odd $n) { say "Number $n is odd" }
multi say-parity(Even) { say "This number is even" } # we don't name the argument, we just put its type
#Also, the last semicolon in a block is optional
对于第一部分,是的,那将是Pascal,它有整数子范围。Modula 3有子范围类型。(序数的子范围。)因此对于示例1,如果您愿意将概率映射到某个精度的整数范围,您可以使用以下方法:
TYPE PROBABILITY = [0..100]
根据需要添加有效数字
参考:关于子范围序数的更多信息。支持与您所说非常类似的内容。例如:
type natural is (int x) where x >= 0
type probability is (real x) where 0.0 <= x && x <= 1.0
该语言非常有表现力。这些不变量和前置/后置条件使用SMT解算器进行静态验证。这可以很好地处理上述示例,但目前难以处理涉及数组和循环不变量的更复杂示例。对于任何感兴趣的人,我想我可以添加一个示例,说明如何解决此问题n截至2019年
问题的第一部分是直截了当的,因为在问这个问题的时间间隔内,Nim已经能够在浮点上生成子范围类型(以及序数和枚举类型)。下面的代码定义了两个新的浮点子范围类型,Probability
和ProbOne
问题的第二部分更为棘手——定义一个类型,并对其字段的函数进行约束。我提出的解决方案没有直接定义这种类型,而是使用宏(makePmf
)来绑定常量表[t,Probability]的创建
object以创建有效的ProbOne
对象(从而确保PMF有效)。编译时对makePmf
宏进行评估,确保您不能创建无效的PMF表
请注意,我是Nim的新手,因此这可能不是编写此宏的最惯用方法:
import macros, tables
type
Probability = range[0.0 .. 1.0]
ProbOne = range[1.0..1.0]
macro makePmf(name: untyped, tbl: untyped): untyped =
## Construct a Table[T, Probability] ensuring
## Sum(Probabilities) == 1.0
# helper templates
template asTable(tc: untyped): untyped =
tc.toTable
template asProb(f: float): untyped =
Probability(f)
# ensure that passed value is already is already
# a table constructor
tbl.expectKind nnkTableConstr
var
totprob: Probability = 0.0
fval: float
newtbl = newTree(nnkTableConstr)
# create Table[T, Probability]
for child in tbl:
child.expectKind nnkExprColonExpr
child[1].expectKind nnkFloatLit
fval = floatVal(child[1])
totprob += Probability(fval)
newtbl.add(newColonExpr(child[0], getAst(asProb(fval))))
# this serves as the check that probs sum to 1.0
discard ProbOne(totprob)
result = newStmtList(newConstStmt(name, getAst(asTable(newtbl))))
makePmf(uniformpmf, {"A": 0.25, "B": 0.25, "C": 0.25, "D": 0.25})
# this static block will show that the macro was evaluated at compile time
static:
echo uniformpmf
# the following invalid PMF won't compile
# makePmf(invalidpmf, {"A": 0.25, "B": 0.25, "C": 0.25, "D": 0.15})
注意:使用宏的一个很酷的好处是nimssuggest
(集成到VS代码中)甚至会突出显示创建无效Pmf表的尝试。我相信ADA有类似的功能(子类型约束)。例如,您正在寻找依赖类型的语言-类型可以依赖于值。一些示例包括Idris、Agda和Coq。SQL确实做到了这一点(请参见)嗨,我在LiquidHaskell上工作(在下面的回答中描述),如果能看到您正在使用的程序/应用程序,我将非常好奇(并且非常感激!)(特别是要保留这些约束的代码。)谢谢!Shen()具有此功能。例如,请参见。我在Agda或Coq中所做的与此问题所要求的不同之处在于,细化类型是新类型,而不是现有类型的子类型。例如,DPD
将是包含映射和一些证明的新类型,而不是恰好满足某些附带条件的映射。@П谢谢---回答已被接受!我原以为Agda能做到。不幸的是,我发现即使是最简单的Agda也无法穿透(我只是在托儿所的slo上)
type natural is (int x) where x >= 0
type probability is (real x) where 0.0 <= x && x <= 1.0
function abs(int x) => (int r)
ensures r >= 0:
//
if x >= 0:
return x
else:
return -x
import macros, tables
type
Probability = range[0.0 .. 1.0]
ProbOne = range[1.0..1.0]
macro makePmf(name: untyped, tbl: untyped): untyped =
## Construct a Table[T, Probability] ensuring
## Sum(Probabilities) == 1.0
# helper templates
template asTable(tc: untyped): untyped =
tc.toTable
template asProb(f: float): untyped =
Probability(f)
# ensure that passed value is already is already
# a table constructor
tbl.expectKind nnkTableConstr
var
totprob: Probability = 0.0
fval: float
newtbl = newTree(nnkTableConstr)
# create Table[T, Probability]
for child in tbl:
child.expectKind nnkExprColonExpr
child[1].expectKind nnkFloatLit
fval = floatVal(child[1])
totprob += Probability(fval)
newtbl.add(newColonExpr(child[0], getAst(asProb(fval))))
# this serves as the check that probs sum to 1.0
discard ProbOne(totprob)
result = newStmtList(newConstStmt(name, getAst(asTable(newtbl))))
makePmf(uniformpmf, {"A": 0.25, "B": 0.25, "C": 0.25, "D": 0.25})
# this static block will show that the macro was evaluated at compile time
static:
echo uniformpmf
# the following invalid PMF won't compile
# makePmf(invalidpmf, {"A": 0.25, "B": 0.25, "C": 0.25, "D": 0.15})