haskell - 在 haskell 中使用替代前奏
问题描述
我对替代前奏很感兴趣。我知道有很多选择:
- https://hackage.haskell.org/packages/#cat:Prelude
- https://guide.aelve.com/haskell/alternative-preludes-zr69k1hc
我知道他们中的很多人修复的一件简单的事情是文本,另一件是在类似head
错误的函数中,当你可能更喜欢它们更安全时。
但是,当我尝试使用这些替代方法时head
,hmm 中的行为似乎完全破坏了该功能,并且对我来说看起来不像是一种改进。这里有些例子:
序幕
Prelude> head [1]
1
Prelude> head []
*** Exception: Prelude.head: empty list
基础
Foundation> head [1]
<interactive>:6:6: error:
• Couldn't match expected type ‘NonEmpty c’
with actual type ‘[Integer]’
• In the first argument of ‘head’, namely ‘[1]’
In the expression: head [1]
In an equation for ‘it’: it = head [1]
• Relevant bindings include
it :: foundation-0.0.21:Foundation.Collection.Element.Element c
(bound at <interactive>:6:1)
Foundation> head []
<interactive>:7:6: error:
• Couldn't match expected type ‘NonEmpty c’ with actual type ‘[a0]’
• In the first argument of ‘head’, namely ‘[]’
In the expression: head []
In an equation for ‘it’: it = head []
• Relevant bindings include
it :: foundation-0.0.21:Foundation.Collection.Element.Element c
(bound at <interactive>:7:1)
安全的
Safe> head []
<interactive>:22:1: error: Variable not in scope: head :: [a0] -> t
优雅的前奏曲
ClassyPrelude> head [1]
<interactive>:24:6: error:
• Couldn't match expected type ‘NonNull mono’
with actual type ‘[Integer]’
• In the first argument of ‘head’, namely ‘[1]’
In the expression: head [1]
In an equation for ‘it’: it = head [1]
• Relevant bindings include
it :: Element mono (bound at <interactive>:24:1)
回避
Relude> head [1]
<interactive>:27:6: error:
• Couldn't match expected type ‘NonEmpty a’
with actual type ‘[Integer]’
• In the first argument of ‘head’, namely ‘[1]’
In the expression: head [1]
In an equation for ‘it’: it = head [1]
• Relevant bindings include it :: a (bound at <interactive>:27:1)
里约
RIO> head [1]
<interactive>:7:1: error:
Variable not in scope: head :: [Integer] -> t
序曲
Protolude> head [1]
Just 1
Protolude> head []
Nothing
这看起来不错——它也适用于尾巴,对吧?
Protolude> tail [1]
<interactive>:12:1: error:
• Variable not in scope: tail :: [Integer] -> t
• Perhaps you meant ‘tails’ (imported from Protolude)
Protolude> tails [1]
[[1],[]]
Protolude> tails []
[[]]
好吧,这不完全是一个替代品。
我错过了什么,为什么这会更好,为什么这些函数会在它们失败的情况下被定义?
解决方案
在大多数情况下,引入它们是因为它们在编译时而不是运行时失败。
问题Prelude.head
不在于(仅)它可能会失败。它必须这样做,因为没有办法获取一个列表[a]
并总是产生一个元素a
,因为输入列表可能是空的。没有简单的解决方法可以直接替代,需要进行彻底的改变。
更安全且可以说更好的前奏可以通过以下方式之一解决此问题:
remove
head
,这样程序员就不会使用危险的工具。head
在编译时,任何使用都会失败。不是很好,但还可以。限制输入类型,例如
head :: NonEmptyList a -> a
. 这将是可用的,但程序员必须调整代码以保证输入列表确实是非空的。仅仅传递一个非空列表对编译器来说是行不通的——编译器想要一个证明,这是正确的。好消息是之前的代码会出现编译错误,这将有助于程序员发现程序中需要修复的部分。限制输出类型,例如
head :: [a] -> Maybe a
. 这可以很好地使用,但程序员将需要处理不同的结果类型,并处理所有潜在Nothing
的 s。同样,编译时错误将帮助程序员确定需要修复的地方。
在任何情况下,程序员都必须修改代码。没有办法解决它。但是,一旦解决了编译时错误,就可以保证程序head: empty list
在运行时永远不会产生错误。
推荐阅读
- install4j - 如何创建能够同时运行多个实例的 Install4J 安装程序?
- ruby-on-rails - 为多租户应用程序轨道运行自定义 rake 任务
- reactjs - 反应开发工具中的钩子支持?
- java - 透明背景 WebEngine javafx
- azure-active-directory - IdP 不断更改签名 x509
- sql-server - 由于自动生成的问题替换 SQL Server IDENTITY
- c - 如何在函数调用和参数中使用命令行参数?
- sql - 查询“有效”但产生错误:标量子查询产生了多个元素
- django - 在 django 中进行内部连接查询集时获取所有字段
- mysql - 使用 JDBC 的 Spring 会话 | 校长为空