我认为当给定指定类型的参数时,函数永远不会失败。
但是看看这个看似无害的代码:
readInts :: String -> [Int]
readInts = map read . words
它的类型签名过于笼统,事实上,对于任何不是由空格分隔的整数组成的字符串,它都会失败,其类型应为:
readInts :: SpaceSeparatedIntegersString -> [Int]
当您尝试构建不符合规定标准的SpaceSeparatedIntegersString
时程序失败。
实施SpaceSeparatedIntegersString
一个明智的想法吗?
如果是这样我可以实现这样的类型? (我只是向正确的方向询问一般性的想法/提示/推动,而不是完整的代码)
如果String
格式不正确(也就是说,我在问题开头的陈述是错误的),我是否应该接受我的函数会失败?
我应该在函数定义中使用保护子句吗?
答案 0 :(得分:8)
你的目标很高尚,但需要非常精确的类型。依赖类型,例如Agda,Coq,Idris和其他语言中提供的类型可以实现您的建议。例如。在Coq,
Definition SpaceSeparatedIntegersString: Type :=
{ s: string | spaceSeparatedIntegers s } .
Definition spaceSeparatedIntegers (s: String): Prop := ...
问题在于,无论谁想要构建SpaceSeparatedIntegersString
值,都必须提供相关属性的正式证明。这是可行的,但需要一些关心,时间和数学技能。
在Haskell中,将类型保证移动到输出类型更为常见。我们可以使输出类型更弱,而不是使输入类型更强:
readInts :: String -> Maybe [Int]
这不是那么精确,但可以在不必证明任何事情的情况下使用。
或者,通过在模块中声明它而不导出其值构造函数,使SpaceSeparatedIntegersString
成为不透明类型。
module Foo(SpaceSeparatedIntegersString(), ...)
data SpaceSeparatedIntegersString = S String -- S is private
这样,模块的用户只能通过模块导出的功能来操作它。需要注意的是,可以制作一堆(导出的)组合器,以保证类型SpaceSeparatedIntegersString
的值永远不会包含无效字符串。
例如,
combine :: SpaceSeparatedIntegersString
-> SpaceSeparatedIntegersString
-> SpaceSeparatedIntegersString
combine (S x) (S y) = S (x ++ " " ++ y)
这并非完全直截了当 - 如果你想允许一般的字符串操作,在某些情况下你最终可能会返回Maybe ...
。
答案 1 :(得分:4)
您可能不应该实施SpaceSeparatedIntegerString
。如果你做了抽象数据类型将是最简单的事情。你不应该接受你的功能应该失败,至少不是在"模式匹配失败"感。你应该创建一个返回类似Maybe [Int]
。
您不应该实施SpaceSeparatedIntegerString
的原因是可能只是将问题推到别处。你可能从一些输入中得到了String
,所以在某个地方你需要一个函数String -> SpaceSeparatedIntegerString
,这也可能会失败。在某些时候,您需要从结构较少的数据转换为更结构化的数据,除非您的唯一值是完全静态指定的。
从结构较少的数据到更结构化的数据(或更少类型的数据到更多类型的数据)的关键点实际上总是部分操作。实际上,解析是将较少结构化数据转换为更结构化数据的过程。但是, 应该做什么,符合您的原则,就像将实现的 reify 恢复到的结构体系一样。因此isValidUri :: String -> Bool
与makeUri :: String -> Maybe String
一样糟糕,但makeUri :: String -> Maybe Uri
其中Uri
是一种可以完全代表有效URI的类型。
答案 2 :(得分:3)
不,在这种情况下,这不是一个明智的想法。 Ian Henry's comment使用Maybe
建议的方法是几个合理的方法之一。你的想法在这种情况下不合理的原因是像readInts
这样的函数的整个目的是处理来自用户的输入,文件,HTTP请求等,这可能不有效。如果您询问用户他们想要购买多少辆汽车并且他们用小猫的照片回复,那么类型系统无法帮助您。 Derek Elkins真的更好地解释了这一点。一旦输入完全验证,chi的依赖类型方法对于其他一些情况非常有用。