如何使用以下签名定义函数
f :: [Int???] -> [Int]
f xs = _ -- what I do with xs doesn't matter for the question
其中a
是Int
的列表
这样第一个参数的输入,即列表元素,必须是>= 0
,但在编译时必须是<= 5
?
换句话说,
f [6]
无法编译。
答案 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质量的批评,而是指出它也不能像问题那样解决暂停问题。