[英]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 a
或Real a
等)。 它们不适用于您的原始示例。 “扩展默认规则”更频繁地应用以避免在交互式解释器中需要大量类型签名(因为您一次输入一行,而不是编译器能够看到完整的模块以从使用中推断类型)。 在这种情况下,在解释器提示符下输入print (safeSearch [] 5)
将起作用,因为它将返回的类型默认为Maybe ()
,并且恰好打印Nothing:: Maybe ()
产生相同的 output ,如果它会正确地猜到了你真正想要的类型。
但在几乎所有实际程序中,将类型变量默认为()
将是一件愚蠢的事情,它会使事情变得更少,所以我不建议养成在实际模块中启用ExtendedDefaultRules
的习惯。 只需添加类型注释,或在解释器中而不是在模块中进行快速检查。
您编写的内容适用于任何真实世界的用例。 只有当有人写print (safeSearch [] x)
时它才会失败——一个文字空列表,没有上下文来说明预期的结果类型。 如果他们传入一个非空列表,或者一个恰好计算为空列表的列表表达式,或者如果他们以一种让类型推断确定其意图的方式使用结果,它就可以正常工作。
此外,确实没有办法编写 function 以便在传递无上下文空列表时它可以工作。 使类型清晰的负担必须放在调用站点上,而不是定义上。 对您问题的评论已经说明了如何执行此操作; 当你以一种显然无用的方式调用你的 function 时,你只需要那么明确。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.