从上下文中获取价值还是留在其中?

时间:2018-08-01 06:02:57

标签: haskell

假设下面的代码。有没有一种更快的方法来从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)

1 个答案:

答案 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或诸如casemaybe之类的全部函数一起使用,则他们可以使用诸如

fromMaybe

这不仅更加清晰,而且确保了任何意外的误用都不会引起类型检查,并且可以更早地发现潜在的错误。

对于一些具体的示例,如果您仅坚持使用全部功能,那么类型系统将如何成为强大的盟友,以及一些思考如何将域的类型安全编码为Haskell的类型系统的好方法,我高度评价建议您阅读马特·帕森斯(Matt Parsons)的精彩博客文章Type Safety Back and Forth,其中将更深入地探讨这些想法。它还重点介绍了如何将fromMaybe 0 n 用作故障的全面描述,并说明如何使用类型系统强制执行先决条件,而无需在整个系统中传播Maybe