繁体   English   中英

为什么 Maybe 返回类型会导致崩溃?

[英]Why does the Maybe return type make this crash?

我限制自己使用预内置函数进行培训。 我已将长度重新编码为计数并且有效。

我有一个搜索功能,当给定索引和列表时,它只返回列表中索引处的值。 它工作得很好。 当索引太大时会抛出错误。

search [] _ = error "index too large"
search (a:_) 0 = a
search (_:a) b = search a (b - 1)

现在,我想要一个safeSearch function 如果索引太大或者列表为空则返回Nothing 所以我只是这样做了。

safeSearch :: [a] -> Int -> Maybe a
safeSearch a b
  | b < 0 || b >= count a = Nothing
  | otherwise = Just (search a b)

它有效。 ...只要您不在空列表上尝试。 即使索引对于列表长度来说太大了。

main = print(safeSearch [] 5)

这崩溃了,我真的找不到任何解决方法。

尽管我认为我的第二行没有用(因为如果列表为空,它的计数为 0,所以我们放入第一个守卫并且它应该返回 Nothing?)它不起作用。 删除它并不能解决问题。

这是编译时错误。

main.hs:91:8: error:
    * Ambiguous type variable `a0' arising from a use of `print'
      prevents the constraint `(Show a0)' from being solved.
      Probable fix: use a type annotation to specify what `a0' should be.
      These potential instances exist:
        instance Show Ordering -- Defined in `GHC.Show'
        instance Show Integer -- Defined in `GHC.Show'
        instance Show a => Show (Maybe a) -- Defined in `GHC.Show'
        ...plus 22 others
        ...plus 13 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    * In the expression: print (safeSearch [] 5)
      In an equation for `main': main = print (safeSearch [] 5)
   |
91 | main = print(safeSearch [] 5)
   |        ^^^^^^^^^^^^^^^^^^^^^^
exit status 1

任何想法? 我错过了什么,甚至完全出错了? 一个我需要更深入理解的概念?

问题是编译错误。 这意味着它实际上并没有运行您的代码并触发了您的error "index too large"调用; 编译器甚至在尝试运行它之前就拒绝了您的代码。 因此,如果您试图更改代码以避免出现这种情况,那么您找错地方了。

实际发生的是safeSearch [] 5返回一个Maybe a类型的值,其中a是列表中元素的类型。 但是您没有在列表中包含任何元素,因此根本无法确定a是什么类型。

您的 function safeSearch可以用于任何类型,所以这实际上很好。 但是您还尝试print Maybe a值。 使用print需要一个Show实例,而Maybe a的实例还需要a Show实例。 因为没有说明a是什么类型,所以编译器无法为它找到合适的Show实例,所以它不得不中止编译并报错。

解决它的最直接的方法是添加一个类型注释(列表或Maybe a safeSearch产生的值)。 是这样的:

main = print (safeSearch ([] :: [Int]) 5)

(这就是错误消息所说的,当它说一个不明确的类型变量正在阻止解决Show约束时,可能的解决方法是添加一个类型注释)

请注意,此类问题在“真实”代码中很少出现。 通常,如果您将列表处理成具有相关类型的另一个结构,您将有其他代码对元素或结果执行某些操作,或者生成列表(并不总是空的)。 除了这些类型的快速测试之外,您通常不会编写一个只处理始终为空的列表并打印结果的程序。 所以通常情况下,当还有其他代码时,编译器将有足够的上下文来推断空列表的类型,并且不需要额外的类型注释。 所以这种额外的类型注释通常不被认为是需要避免的严重负担,因为在“真实”代码中几乎不需要它们。 您只需按照自己的意愿进行编码,并且在编译错误的情况下让您意识到您需要一个注释,您只需添加它并继续。

如果您在 GHCi 中进行这种快速检查而不是使用main function 编写完整程序,那么您也不需要额外的类型注释。 这是因为 GHCi 默认开启了ExtendedDefaultRules语言扩展。 “默认规则”是 GHC 为您选择类型而不是抛出“模糊类型”错误的条件。 正常的默认规则非常严格,实际上仅设计用于默认数字约束(如Num aReal a等)。 它们不适用于您的原始示例。 “扩展默认规则”更频繁地应用以避免在交互式解释器中需要大量类型签名(因为您一次输入一行,而不是编译器能够看到完整的模块以从使用中推断类型)。 在这种情况下,在解释器提示符下输入print (safeSearch [] 5)将起作用,因为它将返回的类型默认为Maybe () ,并且恰好打印Nothing:: Maybe ()产生相同的 output ,如果它会正确地猜到了你真正想要的类型。

但在几乎所有实际程序中,将类型变量默认为()将是一件愚蠢的事情,它会使事情变得更少,所以我不建议养成在实际模块中启用ExtendedDefaultRules的习惯。 只需添加类型注释,或在解释器中而不是在模块中进行快速检查。

您编写的内容适用于任何真实世界的用例。 只有当有人写print (safeSearch [] x)时它才会失败——一个文字空列表,没有上下文来说明预期的结果类型。 如果他们传入一个非空列表,或者一个恰好计算为空列表的列表表达式,或者如果他们以一种让类型推断确定其意图的方式使用结果,它就可以正常工作。

此外,确实没有办法编写 function 以便在传递无上下文空列表时它可以工作。 使类型清晰的负担必须放在调用站点上,而不是定义上。 对您问题的评论已经说明了如何执行此操作; 当你以一种显然无用的方式调用你的 function 时,你只需要那么明确。

暂无
暂无

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

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