在列表元素上定义约束函数?

时间:2017-05-19 11:20:37

标签: haskell

如何使用以下签名定义函数

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

其中aInt的列表

这样第一个参数的输入,即列表元素,必须是>= 0,但在编译时必须是<= 5

换句话说,

f [6]无法编译。

2 个答案:

答案 0 :(得分:5)

怎么样:

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

或者您是否要强制执行类型(依赖类型)的界限?

如果您想限制允许的Int范围,则可能更适合使用智能构造函数。看看here。这个想法是你创建自己的数据类型和自己的自定义构造函数:

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

如果您创建智能构造函数,则不要将其公开给模块的用户。这可以通过简单地不导出Range0_5构造函数来完成。

然而,这不是编译时检查。如果你真的需要这样的功能,那么除了Haskell之外的其他语言可能更合适。

由于范围相当小,您还可以使用和类型来表示它:

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

答案 1 :(得分:1)

如果签名是

f :: [Int] -> [Int]

(这是问题的原始形式),然后在编译时强制执行约束是不可能的。这符合标准diagonalization argument of the Halting problem

假设编译器可以检测到

f[g x]

不应该编译。通过将编译器的源代码合并到g中,它可以选择与编译器决策相反的。

根据您对Liquid Haskell的评论(这似乎是一个非常有趣的项目),请注意以下内容:

{-@ 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声称此功能不安全,因为可能会foo n调用foo (n - 1)。但请注意,这种情况永远不会发生:只有当关系 n 2 - 1≠(n + 1)(n - 1)时才会调用它,永远不会发生。

同样,这不是对LiquidHaskell质量的批评,而是指出它也不能像问题那样解决暂停问题。