简体   繁体   English

正整数类型

[英]Positive integer type

In many articles about Haskell they say it allows to make some checks during compile time instead of run time.在许多关于 Haskell 的文章中,他们说它允许在编译时而不是运行时进行一些检查。 So, I want to implement the simplest check possible - allow a function to be called only on integers greater than zero.所以,我想实现最简单的检查——只允许对大于零的整数调用函数。 How can I do it?我该怎么做?

module Positive (toPositive, getPositive, Positive) where

newtype Positive = Positive { unPositive :: Int }

toPositive :: Int -> Maybe Positive
toPositive n = if (n <= 0) then Nothing else Just (Positive n)

-- We can't export unPositive, because unPositive can be used
-- to update the field.  Trivially renaming it to getPositive
-- ensures that getPositive can only be used to access the field
getPositive :: Positive -> Int
getPositive = unPositive

The above module doesn't export the constructor, so the only way to build a value of type Positive is to supply toPositive with a positive integer, which you can then unwrap using getPositive to access the actual value.上面的模块没有导出构造函数,因此构建Positive类型值的唯一方法是向toPositive提供一个正整数,然后您可以使用getPositive解包以访问实际值。

You can then write a function that only accepts positive integers using:然后,您可以使用以下方法编写一个仅接受正整数的函数:

positiveInputsOnly :: Positive -> ...

Haskell can perform some checks at compile time that other languages perform at runtime. Haskell 可以在编译时执行一些其他语言在运行时执行的检查。 Your question seems to imply you are hoping for arbitrary checks to be lifted to compile time, which isn't possible without a large potential for proof obligations (which could mean you, the programmer, would need to prove the property is true for all uses).您的问题似乎暗示您希望在编译时取消任意检查,如果没有很大的证明义务潜力,这是不可能的(这可能意味着您,程序员,需要证明该属性适用于所有用途)。

In the below, I don't feel like I'm saying anything more than what pigworker touched on while mentioning the very cool sounding Inch tool.在下面,我不觉得我说的只是 pigworker 在提到听起来非常酷的Inch工具时所触及的内容。 Hopefully the additional words on each topic will clarify some of the solution space for you.希望关于每个主题的附加词将为您阐明一些解决方案空间。

What People Mean (when speaking of Haskell's static guarantees)人们的意思(当谈到 Haskell 的静态保证时)

Typically when I hear people talk about the static guarantees provided by Haskell they are talking about the Hindley Milner style static type checking.通常,当我听到人们谈论 Haskell 提供的静态保证时,他们谈论的是 Hindley Milner 风格的静态类型检查。 This means one type can not be confused for another - any such misuse is caught at compile time (ex: let x = "5" in x + 1 is invalid).这意味着不能将一种类型与另一种类型混淆 - 在编译时会发现任何此类滥用(例如: let x = "5" in x + 1是无效的)。 Obviously, this only scratches the surface and we can discuss some more aspects of static checking in Haskell.显然,这只是皮毛,我们可以讨论 Haskell 中静态检查的更多方面。

Smart Constructors: Check once at runtime, ensure safety via types智能构造器:运行时检查一次,通过类型确保安全

Gabriel's solution is to have a type, Positive , that can only be positive. Gabriel 的解决方案是有一个类型Positive ,它只能是正数。 Building positive values still requires a check at runtime but once you have a positive there are no checks required by consuming functions - the static (compile time) type checking can be leveraged from here.构建正值仍然需要在运行时进行检查,但是一旦获得肯定,使用函数就不需要检查 - 可以从这里利用静态(编译时)类型检查。

This is a good solution for many many problems.对于许多问题,这是一个很好的解决方案。 I recommended the same thing when discussing golden numbers .我在讨论黄金数字时推荐了同样的东西。 Never-the-less, I don't think this is what you are fishing for.无论如何,我认为这不是您要钓鱼的目的。

Exact Representations精确表示

dflemstr commented that you can use a type, Word , which is unable to represent negative numbers (a slightly different issue than representing positives). dflemstr 评论说您可以使用Word类型,它无法表示负数(与表示正数略有不同的问题)。 In this manner you really don't need to use a guarded constructor (as above) because there is no inhabitant of the type that violates your invariant.通过这种方式,您实际上不需要使用受保护的构造函数(如上所述),因为没有违反您的不变量的类型的居民。

A more common example of using proper representations is non-empty lists.使用正确表示的一个更常见的例子是非空列表。 If you want a type that can never be empty then you could just make a non-empty list type:如果您想要一个永远不会为空的类型,那么您可以创建一个非空列表类型:

data NonEmptyList a = Single a | Cons a (NonEmptyList a)

This is in contrast to the traditional list definition using Nil instead of Single a .这与使用Nil而不是Single a的传统列表定义形成对比。

Going back to the positive example, you could use a form of Peano numbers:回到正面的例子,你可以使用 Peano 数的一种形式:

data NonNegative = One | S NonNegative

Or user GADTs to build unsigned binary numbers (and you can add Num , and other instances, allowing functions like + ):或用户 GADT 构建无符号二进制数(您可以添加Num和其他实例,允许使用+等函数):

{-# LANGUAGE GADTs #-}

data Zero
data NonZero
data Binary a where
  I :: Binary a -> Binary NonZero
  O :: Binary a -> Binary a
  Z :: Binary Zero
  N :: Binary NonZero

instance Show (Binary a) where
        show (I x) = "1" ++ show x
        show (O x) = "0" ++ show x
        show (Z)   = "0" 
        show (N)   = "1"

External Proofs外部证明

While not part of the Haskell universe, it is possible to generate Haskell using alternate systems (such as Coq) that allow richer properties to be stated and proven.虽然不是 Haskell 世界的一部分,但可以使用替代系统(例如 Coq)生成 Haskell,这些系统允许陈述和证明更丰富的属性。 In this manner the Haskell code can simply omit checks like x > 0 but the fact that x will always be greater than 0 will be a static guarantee (again: the safety is not due to Haskell).以这种方式,Haskell 代码可以简单地省略像x > 0这样的检查,但是 x 总是大于 0 的事实将是一个静态保证(再次强调:安全性不是由于 Haskell)。

From what pigworker said, I would classify Inch in this category.根据猪工所说,我会将Inch归为这一类。 Haskell has not grown sufficiently to perform your desired tasks, but tools to generate Haskell (in this case, very thin layers over Haskell) continue to make progress. Haskell 还没有发展到足以执行您想要的任务,但是生成 Haskell 的工具(在这种情况下,是 Haskell 上的非常薄的层)继续取得进展。

Research on More Descriptive Static Properties更具描述性的静态属性研究

The research community that works with Haskell is wonderful.与 Haskell 合作的研究社区非常棒。 While too immature for general use, people have developed tools to do things like statically check function partiality and contracts .虽然对于一般用途来说还太不成熟,但人们已经开发出一些工具来执行诸如静态检查函数偏向性合同之类的事情。 If you look around you'll find it's a rich field.如果你环顾四周,你会发现这是一个丰富的领域。

I would be failing in my duty as his supervisor if I failed to plug Adam Gundry's Inch preprocessor, which manages integer constraints for Haskell.如果我未能插入 Adam Gundry 的Inch预处理器,该预处理器管理 Haskell 的整数约束,我将无法履行作为他的主管的职责。

Smart constructors and abstraction barriers are all very well, but they push too much testing to run time and don't allow for the possibility that you might actually know what you're doing in a way that checks out statically, with no need for Maybe padding.智能构造函数和抽象障碍都非常好,但是它们将过多的测试推向运行时间,并且不允许您实际上以静态检查的方式知道自己在做什么,而无需Maybe填充。 (A pedant writes. The author of another answer appears to suggest that 0 is positive, which some might consider contentious. Of course, the truth is that we have uses for a variety of lower bounds, 0 and 1 both occurring often. We also have some use for upper bounds.) (一位学究写道。另一个答案的作者似乎暗示 0 是肯定的,有些人可能会认为这是有争议的。当然,事实是我们使用了各种下限,0 和 1 都经常出现。我们也对上限有一些用处。)

In the tradition of Xi's DML, Adam's preprocessor adds an extra layer of precision on top of what Haskell natively offers but the resulting code erases to Haskell as is.在 Xi 的 DML 的传统中,Adam 的预处理器在 Haskell 原生提供的基础上增加了一层额外的精度,但生成的代码会按原样擦除到 Haskell。 It would be great if what he's done could be better integrated with GHC, in coordination with the work on type level natural numbers that Iavor Diatchki has been doing.如果他的工作能够更好地与 GHC 集成,与 Iavor Diatchki 一直在做的类型级别自然数的工作相协调,那就太好了。 We're keen to figure out what's possible.我们很想弄清楚什么是可能的。

To return to the general point, Haskell is currently not sufficiently dependently typed to allow the construction of subtypes by comprehension (eg, elements of Integer greater than 0), but you can often refactor the types to a more indexed version which admits static constraint.回到一般的观点,Haskell 目前没有足够的依赖类型来允许通过理解来构造子类型(例如,大于 0 的 Integer 元素),但您通常可以将类型重构为允许静态约束的更多索引版本。 Currently, the singleton type construction is the cleanest of the available unpleasant ways to achieve this.目前,单例类型的构造是实现这一目标的可用方法中最干净的。 You'd need a kind of "static" integers, then inhabitants of kind Integer -> * capture properties of particular integers such as "having a dynamic representation" (that's the singleton construction, giving each static thing a unique dynamic counterpart) but also more specific things like "being positive".你需要一种“静态”整数,然后是Integer -> *类型的居民捕获特定整数的属性,例如“具有动态表示”(这是单例构造,为每个静态事物提供唯一的动态对应物)但也更具体的事情,比如“积极”。

Inch represents an imagining of what it would be like if you didn't need to bother with the singleton construction in order to work with some reasonably well behaved subsets of the integers.英寸代表了一种想象,如果您不需要为单例构造而烦恼,以便使用一些表现良好的整数子集,那会是什么样子。 Dependently typed programming is often possible in Haskell, but is currently more complicated than necessary.在 Haskell 中,依赖类型编程通常是可能的,但目前比必要的要复杂得多。 The appropriate sentiment toward this situation is embarrassment, and I for one feel it most keenly.对这种情况的恰当情绪是尴尬,而我对此感受最为强烈。

I know that this was answered a long time ago and I already provided an answer of my own, but I wanted to draw attention to a new solution that became available in the interim: Liquid Haskell, which you can read an introduction to here .我知道很久以前就已经回答了这个问题,并且我已经提供了自己的答案,但我想提请注意临时可用的新解决方案:Liquid Haskell,您可以在此处阅读介绍。

In this case, you can specify that a given value must be positive by writing:在这种情况下,您可以通过以下方式指定给定值必须为正:

{-@ myValue :: {v: Int | v > 0} #-}
myValue = 5

Similarly, you can specify that a function f requires only positive arguments like this:类似地,您可以指定函数f只需要像这样的正参数:

{-@ f :: {v: Int | v > 0 } -> Int @-}

Liquid Haskell will verify at compile-time that the given constraints are satisfied. Liquid Haskell 将在编译时验证给定的约束是否得到满足。

This—or actually, the similar desire for a type of natural numbers (including 0)—is actually a common complaints about Haskell's numeric class hierarchy, which makes it impossible to provide a really clean solution to this.这——或者实际上,对自然数类型(包括 0)的类似需求——实际上是对 Haskell 数字类层次结构的常见抱怨,这使得它不可能提供一个真正干净的解决方案。

Why?为什么? Look at the definition of Num :查看Num的定义:

class (Eq a, Show a) => Num a where
    (+) :: a -> a -> a
    (*) :: a -> a -> a
    (-) :: a -> a -> a
    negate :: a -> a
    abs :: a -> a
    signum :: a -> a
    fromInteger :: Integer -> a

Unless you revert to using error (which is a bad practice), there is no way you can provide definitions for (-) , negate and fromInteger .除非您恢复使用error (这是一种不好的做法),否则您无法为(-)negatefromInteger提供定义。

Type-level natural numbers are planned for GHC 7.6.1: https://ghc.haskell.org/trac/ghc/ticket/4385 GHC 7.6.1 计划使用类型级自然数: https ://ghc.haskell.org/trac/ghc/ticket/4385

Using this feature it's trivial to write a "natural number" type, and gives a performance you could never achieve (eg with a manually written Peano number type).使用此功能,编写“自然数”类型很简单,并提供您永远无法实现的性能(例如,使用手动编写的 Peano 数类型)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM