是否有可约束类型的语言?

时间:2013-10-04 10:26:25

标签: haskell agda dependent-type

是否有类型编程语言,我可以约束类型,如下面两个例子?

  1. 概率是浮点数,最小值为0.0,最大值为1.0。

    type Probability subtype of float
    where
        max_value = 0.0
        min_value = 1.0
    
  2. 离散概率分布是一个映射,其中:键应该都是相同的类型,值都是概率,值的总和= 1.0。

    type DPD<K> subtype of map<K, Probability>
    where
        sum(values) = 1.0
    
  3. 据我所知,Haskell或Agda无法做到这一点。

8 个答案:

答案 0 :(得分:30)

您想要的是refinement types

可以在Agda中定义ProbabilityProb.agda

具有和条件的概率质量函数类型在第264行定义。

有些语言具有比Agda更直接的细化类型,例如ATS

答案 1 :(得分:25)

您可以在Haskell中使用Liquid Haskell执行此操作,该refinement types使用{{3}}扩展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

(您可能希望将[]包裹在newtype中。)

现在,您可以在细化中使用total来约束列表:

{dist: [(a, Float)] | total dist == 1}

Liquid Haskell的巧妙之处在于,所有推理都是在编译时自动完成的,以换取使用有限约束的逻辑。 (像total这样的度量在编写它们的方式上也受到很大限制 - 它是Haskell的一个小子集,其规则如“每个构造函数只有一个案例”。)这意味着这种风格的细化类型不那么强大但是很多比完全依赖类型更容易使用,使它们更实用。

答案 2 :(得分:9)

Perl6有一个&#34;类型子集的概念&#34;这可以添加任意条件来创建&#34;子类型。&#34;

具体问题:

subset Probability of Real where 0 .. 1;

role DPD[::T] {
  has Map[T, Probability] $.map
    where [+](.values) == 1; # calls `.values` on Map
}

(注意:在当前的实现中,&#34;其中&#34;部分在运行时被检查,但是因为&#34;真实类型&#34;在编译时被检查(包括你的类) ,并且因为在std中有纯注释(is pure)(主要是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(或多种,真的 - 多种方法或其他方法),我们可以根据这一点进行调度:

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

答案 3 :(得分:7)

Nimrod是一种支持这一概念的新语言。它们被称为子范围。这是一个例子。您可以在此处了解有关该语言的更多信息link

type
  TSubrange = range[0..5]

答案 4 :(得分:2)

对于第一部分,是的,那将是Pascal,它具有整数子范围。

答案 5 :(得分:2)

Whiley language支持非常类似于你所说的内容。例如:

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

语言非常富有表现力。使用SMT求解器静态验证这些不变量和前/后条件。这很好地处理了上面的例子,但目前正在努力处理涉及数组和循环不变量的更复杂的例子。

答案 6 :(得分:0)

对于有兴趣的人,我想在2019年之前在Nim中添加一个示例,说明您如何解决此问题。

问题的第一部分是直截了当的,因为自提出该问题以来的时间间隔内,Nim获得了在浮点数上生成子范围类型(​​以及序数和枚举类型)的能力。下面的代码定义了两个新的float子范围类型,ProbabilityProbOne

问题的第二部分比较棘手-定义一个类型,该类型受其字段功能的约束。我提出的解决方案没有直接定义这种类型,而是使用宏(makePmf)将常量Table[T,Probability]对象的创建与创建有效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})

注意:使用宏的一个很酷的好处是nimsuggest(集成到VS Code中)甚至会突出显示创建无效Pmf表的尝试。

答案 7 :(得分:-1)

Modula 3具有子范围类型。 (序数的子范围。)因此,对于示例1,如果您愿意将概率映射到某个精度的整数范围,则可以使用:

TYPE PROBABILITY = [0..100]

根据需要添加有效数字。

参考:关于子范围here的更多信息。