简体   繁体   中英

Define Function with Constraint on List's Elements?

How can I define a function with the following signature,

f :: [Int???] -> [Int]
f xs = _ -- what I do with xs doesn't matter for the question

where a is a List of Int 's

such that the first argument's inputs, ie list elements, must be >= 0 , but <= 5 at compile-time?

In other words,

f [6] would fail to compile.

How about:

f :: [Int] -> [Int]
f = filter (\x -> x >= 0 && x <= 5)

Or do you want to enforce the bounds on the type (dependent types)?

If you want to restrict the range of the Int that is allowed you are probably better of using a smart constructor . Have a look here . The idea is that you create your own datatype and your own custom constructor:

newtype Range0_5 = Range0_5 { unRange :: Int }

makeRange0_5 :: Int -> Maybe Range0_5
makeRange0_5 x
  | x >= 0 && x <= 5 = Just $ Range0_5 x
  | otherwise        = Nothing

If you make a smart constructor , it is important to not expose it to the user of the module. This can be done by simply not exporting the Range0_5 constructor.

However this is not a compile time check. Other languages than Haskell might be more appropriate if you really need such a feature.

Since the range is fairly small, you could also make a sum type to represent it:

data Range0_5 = Int0 | Int1 | Int2 | Int3 | Int4 | Int5

If the signature is

f :: [Int] -> [Int]

(which was the original form of the question), then it is impossible to enforce your constraint at compile time. This follows from the standard diagonalization argument of the Halting problem .

Suppose the compiler could detect that

f[g x]

should not compile. By incorporating the source code of the compiler into g , it could choose the opposite of the compiler's decision.


Following your comment on Liquid Haskell (which seems like a very interesting project), note the following:

{-@ type Even = {v:Int | v mod 2 = 0} @-}

{-@ foo :: n:Even -> {v:Bool | (v <=> (n mod 2 == 0))} @-}   
foo   :: Int -> Bool 
foo n = if n^2 - 1 == (n + 1) * (n - 1) then True else foo (n - 1)

LiquidHaskell claims this function is unsafe, because, potentially foo n calls foo (n - 1) . Note, however, that this will never happen: it will only be called if the relationship n 2 - 1 ≠ (n + 1) (n - 1) , which can never happen.

Again, this is not a criticism of the quality of LiquidHaskell, but rather just pointing out that it, too, cannot solve Halting Problem like issues.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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