简体   繁体   English

Haskell类型表示另一种类型的子集

[英]Haskell type representing a subset of another type

I think that when given parameter(s) of the specified type, a function should never fail. 我认为当给定指定类型的参数时,函数永远不会失败。

But take this seemingly innocuous code: 但是看看这个看似无害的代码:

readInts :: String -> [Int]
readInts = map read . words

It has a type signature that is too general, in fact, it will fail for any string that is not composed by space separated integers, its type should then be: 它的类型签名过于笼统,事实上,对于任何不是由空格分隔的整数组成的字符串,它都会失败,其类型应为:

readInts :: SpaceSeparatedIntegersString -> [Int]

When the program fails when you try to build a SpaceSeparatedIntegersString that does not respect the stated criteria. 当您尝试构建不遵守规定标准的SpaceSeparatedIntegersString时程序失败。

  • Is implementing a SpaceSeparatedIntegersString a sensible idea? 实现SpaceSeparatedIntegersString是一个明智的想法吗?

  • If it is how can I implement such a type? 如果是我怎样才能实现这样的类型? (I ask just a general idea/tip/nudge in the right direction, not full code) (我只是问一个一般的想法/提示/推动正确的方向,而不是完整的代码)

  • Should I just accept that my function will fail when the String formatting is not correct (that is, my statement at the start of tis question is wrong)? 我是否应该接受当String格式不正确时我的函数将失败(也就是说,我在问题开头的陈述是错误的)?

  • Should I just use a guard clause in the function definition? 我应该在函数定义中使用保护子句吗?

Your goal is noble, but requires very precise types. 你的目标是高尚的,但需要非常精确的类型。 Dependent types, such the ones available in Agda, Coq, Idris, and other languages can achieve what you propose. 依赖类型,例如Agda,Coq,Idris和其他语言中提供的类型可以实现您的建议。 Eg in Coq, 例如在Coq,

Definition SpaceSeparatedIntegersString: Type :=
   { s: string | spaceSeparatedIntegers s } .
Definition spaceSeparatedIntegers (s: String): Prop := ...

The catch is that, whoever wants to construct a SpaceSeparatedIntegersString value, must provide a formal proof of the relevant property. 问题在于,无论谁想要构造SpaceSeparatedIntegersString值,都必须提供相关属性的正式证明。 This is feasible, but requires some care, time and mathematical skills. 这是可行的,但需要一些关心,时间和数学技能。

In Haskell, it is more common to move the type-guarantee to the output type. 在Haskell中,将类型保证移动到输出类型更为常见。 Instead of making the input type stronger, we can make the output type weaker: 我们可以使输出类型更弱,而不是使输入类型更强:

readInts :: String -> Maybe [Int]

This is not as precise, but can be used without having to prove anything. 这不是那么精确,但可以在不必证明任何事情的情况下使用。

Alternatively, make SpaceSeparatedIntegersString be an opaque type by declaring it in a module without exporting its value constructors. 或者,通过在模块中声明它而不导出其值构造函数,使SpaceSeparatedIntegersString成为不透明类型。

module Foo(SpaceSeparatedIntegersString(), ...)
data SpaceSeparatedIntegersString = S String        -- S is private

In this way, the users of the module will have to manipulate it only through functions exported by the module. 这样,模块的用户只能通过模块导出的功能来操作它。 With some care, one can craft a bunch of (exported) combinators which guarantee that values of type SpaceSeparatedIntegersString will never contain invalid strings. 需要注意的是,可以制作一组(导出的)组合器,以保证SpaceSeparatedIntegersString类型的值永远不会包含无效字符串。 For instance, 例如,

combine :: SpaceSeparatedIntegersString 
        -> SpaceSeparatedIntegersString
        -> SpaceSeparatedIntegersString 
combine (S x) (S y) = S (x ++ " " ++ y)

This is not entirely straightforward -- if you want to allow general string manipulation, you will probably end up in returning Maybe ... in some cases. 这并不完全是直截了当的 - 如果你想允许一般的字符串操作,你可能会在某些情况下最终返回Maybe ...

You probably shouldn't implement SpaceSeparatedIntegerString . 您可能不应该实现SpaceSeparatedIntegerString If you did an abstract data type would be the simplest thing to do. 如果你做了抽象数据类型将是最简单的事情。 You should not accept that your function should fail, at least not in the "pattern match failure" sense. 你不应该接受你的功能应该失败,至少不是在“模式匹配失败”的意义上。 You should make a function that returns something like Maybe [Int] . 你应该创建一个返回Maybe [Int]类的函数。

The reason you shouldn't implement SpaceSeparatedIntegerString is that likely just pushes the problem elsewhere. 你不应该实现SpaceSeparatedIntegerString的原因是可能只是将问题推到别处。 You probably are getting the String s from some input, so somewhere you need a function String -> SpaceSeparatedIntegerString and that could fail just as well. 您可能从某些输入获取String ,所以在某处您需要一个函数String -> SpaceSeparatedIntegerString ,这也可能会失败。 At some point you need to go from less structured to more structured data unless your only values are completely statically specified. 在某些时候,您需要从结构较少的数据转换为更结构化的数据,除非您的唯一值是完全静态指定的。

The point is going from less structured data to more structured data (or less typed data to more typed data) is virtually always going to be a partial operation. 从结构较少的数据到更结构化的数据(或者更少类型的数据到更多类型的数据)的关键点实际上总是部分操作。 Indeed, parsing is the process of turning less structured data into more structured data. 实际上,解析是将较少结构化数据转换为更结构化数据的过程。 What you should do, though, in line with your principle, is as much as practical reify the recovered structure into the type system. 应该做的,不过,在与你的原则,就是尽可能的实际具体化回收的结构入式系统。 So isValidUri :: String -> Bool is bad, as is makeUri :: String -> Maybe String , but makeUri :: String -> Maybe Uri where Uri is a type that can represent exactly the valid URIs is good. 所以isValidUri :: String -> Bool是坏的,因为makeUri :: String -> Maybe String ,但是makeUri :: String -> Maybe Uri ,其中Uri是一个可以完全代表有效URI的类型。

No, this is not a sensible idea in this case. 不,在这种情况下,这不是一个明智的想法。 The approach suggested in Ian Henry's comment using Maybe is one of several reasonable ones. Ian Henry使用Maybe 的评论中提出的方法是几个合理的方法之一。 The reason your idea is not sensible in this context is that the entire purpose of a function like readInts is to process input from the user, a file, an HTTP request, etc., which may not be valid. 其原因你的想法是在此上下文中明智的是,像一个函数的全部目的readInts是从用户,文件,HTTP请求等,这可能是无效的处理的输入。 The type system can't help you if you ask the user how many cars they want to buy and they respond with a picture of a kitten. 如果您询问用户他们想购买多少辆汽车并且他们用小猫的照片回复,那么型号系统无法帮助您。 Derek Elkins really explains this better. Derek Elkins真的更好地解释了这一点。 chi's dependently typed approach is great for some other situations, once the input has been fully validated. 一旦输入完全验证,chi的依赖类型方法对于其他一些情况非常有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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