假设下面的代码。有没有一种更快的方法来从findSerial
中获取上下文值,而不是编写类似outOfContext
的函数?
潜在的问题是:通常是否会停留在上下文中并使用Functor,Applicatives,Monoids和Monads来完成工作,还是最好使其脱离上下文并应用通常的非上下文计算方法。简而言之:不想学习所有错误的Haskell,因为它花了足够的时间。
import qualified Data.Map as Map
type SerialNumber = (String, Int)
serialList :: Map.Map String SerialNumber
serialList = Map.fromList [("belt drive",("BD",0001))
,("chain drive",("CD",0002))
,("drive pulley",("DP",0003))
,("drive sprocket",("DS",0004))
]
findSerial :: Ord k => k -> Map.Map k a -> Maybe a
findSerial input = Map.lookup input
outOfContext (Just (a, b)) = (a, b)
答案 0 :(得分:6)
假设我理解正确,我认为您的问题实质上可以归结为“在Haskell中编写和使用partial functions是否是惯用的?”(您的outOfContext
函数是该函数,因为它只是一个专门的内置部分功能fromJust
的形式。该问题的答案是否。尽可能避免使用局部函数,使用局部函数的代码通常可以重构为不使用局部函数的代码。
避免使用部分功能的原因是它们会自愿损害类型系统的有效性。在Haskell中,当函数的类型为X -> Y
时,通常假定向其提供X
实际上会产生一个Y
,并且不会完全做其他事情(即崩溃) 。如果您有并非总是成功的功能,则通过编写X -> Maybe Y
反映类型中的信息会强制调用者以某种方式处理Nothing
情况,并且可以直接处理它,或将故障进一步推迟给 调用者(通过产生Maybe
)。这很棒,因为这意味着进行类型检查的程序不会在运行时崩溃。该程序可能仍然存在逻辑错误,但是即使在运行该程序之前也不会崩溃,这还是很不错的。
部分函数将此保证排除在窗口之外。如果偶然违反了函数的前提条件,则使用部分函数的任何程序都将在运行时崩溃,并且由于这些前提条件未反映在类型系统中,因此编译器无法静态地强制执行它们。在编写程序时,程序在逻辑上可能是正确的,但是如果不使用类型系统强制要求正确性,则进一步的修改,扩展或重构可能很容易错误地引入错误。
例如,程序员可能会编写表达式
if isJust n then fromJust n else 0
它肯定不会在运行时崩溃,因为fromJust
的前提条件总是在调用之前被检查。但是,类型系统无法强制执行此操作,并且进一步的重构可能会交换if
的分支,或者可能会将fromJust n
完全移至程序的其他部分,并意外地忽略了{{1 }}。该程序仍将编译,但可能在运行时失败。
相反,如果程序员避免使用部分函数,而将显式模式匹配与isJust
或诸如case
和maybe
之类的全部函数一起使用,则他们可以使用诸如
fromMaybe
这不仅更加清晰,而且确保了任何意外的误用都不会引起类型检查,并且可以更早地发现潜在的错误。
对于一些具体的示例,如果您仅坚持使用全部功能,那么类型系统将如何成为强大的盟友,以及一些思考如何将域的类型安全编码为Haskell的类型系统的好方法,我高度评价建议您阅读马特·帕森斯(Matt Parsons)的精彩博客文章Type Safety Back and Forth,其中将更深入地探讨这些想法。它还重点介绍了如何将fromMaybe 0 n
用作故障的全面描述,并说明如何使用类型系统强制执行先决条件,而无需在整个系统中传播Maybe
。