Is there a language with constrainable types?

隐身守侯 提交于 2019-11-27 02:33:56

问题


Is there a typed programming language where I can constrain types like the following two examples?

  1. A Probability is a floating point number with minimum value 0.0 and maximum value 1.0.

    type Probability subtype of float
    where
        max_value = 0.0
        min_value = 1.0
    
  2. A Discrete Probability Distribution is a map, where: the keys should all be the same type, the values are all Probabilities, and the sum of the values = 1.0.

    type DPD<K> subtype of map<K, Probability>
    where
        sum(values) = 1.0
    

As far as I understand, this is not possible with Haskell or Agda.


回答1:


What you want is called refinement types.

It's possible to define Probability in Agda: Prob.agda

The probability mass function type, with sum condition is defined at line 264.

There are languages with more direct refinement types than in Agda, for example ATS




回答2:


You can do this in Haskell with Liquid Haskell which extends Haskell with refinement types. The predicates are managed by an SMT solver at compile time which means that the proofs are fully automatic but the logic you can use is limited by what the SMT solver handles. (Happily, modern SMT solvers are reasonably versatile!)

One problem is that I don't think Liquid Haskell currently supports floats. If it doesn't though, it should be possible to rectify because there are theories of floating point numbers for SMT solvers. You could also pretend floating point numbers were actually rational (or even use Rational in Haskell!). With this in mind, your first type could look like this:

{p : Float | p >= 0 && p <= 1}

Your second type would be a bit harder to encode, especially because maps are an abstract type that's hard to reason about. If you used a list of pairs instead of a map, you could write a "measure" like this:

measure total :: [(a, Float)] -> Float
total []          = 0 
total ((_, p):ps) = p + probDist ps

(You might want to wrap [] in a newtype too.)

Now you can use total in a refinement to constrain a list:

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

The neat trick with Liquid Haskell is that all the reasoning is automated for you at compile time, in return for using a somewhat constrained logic. (Measures like total are also very constrained in how they can be written—it's a small subset of Haskell with rules like "exactly one case per constructor".) This means that refinement types in this style are less powerful but much easier to use than full-on dependent types, making them more practical.




回答3:


Perl6 has a notion of "type subsets" which can add arbitrary conditions to create a "sub type."

For your question specifically:

subset Probability of Real where 0 .. 1;

and

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

(note: in current implementations, the "where" part is checked at run-time, but since "real types" are checked at compile-time (that includes your classes), and since there are pure annotations (is pure) inside the std (which is mostly perl6) (those are also on operators like *, etc), it's only a matter of effort put into it (and it shouldn't be much more).

More generally:

# (%% 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")

Then you can check if a number matches with the Smart Matching operator ~~:

say 4 ~~ Even; # True
say 4 ~~ Odd; # False
say 5 ~~ Odd; # True

And, thanks to multi subs (or multi whatever, really – multi methods or others), we can dispatch based on that:

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



回答4:


Nimrod is a new language that supports this concept. They are called Subranges. Here is an example. You can learn more about the language here link

type
  TSubrange = range[0..5]



回答5:


For the first part, yes, that would be Pascal, which has integer subranges.




回答6:


The Whiley language supports something very much like what you are saying. For example:

type natural is (int x) where x >= 0
type probability is (real x) where 0.0 <= x && x <= 1.0

These types can also be implemented as pre-/post-conditions like so:

function abs(int x) => (int r)
ensures r >= 0:
    //
    if x >= 0:
        return x
    else:
        return -x

The language is very expressive. These invariants and pre-/post-conditions are verified statically using an SMT solver. This handles examples like the above very well, but currently struggles with more complex examples involving arrays and loop invariants.




回答7:


Modula 3 has subrange types. (Subranges of ordinals.) So for your Example 1, if you're willing to map probability to an integer range of some precision, you could use this:

TYPE PROBABILITY = [0..100]

Add significant digits as necessary.

Ref: More about subrange ordinals here.



来源:https://stackoverflow.com/questions/19178989/is-there-a-language-with-constrainable-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!