Haskell是否提供了针对许多可能的数据构造函数进行模式匹配的习惯用法?



在一个Haskell项目上工作,我正在处理FSNotify包中的Event数据类型。Event的构造函数都是:

Added FilePath UTCTime
Modified FilePath UTCTime
Removed FilePath UTCTime

在我的代码中,我只对从Event中提取FilePath感兴趣,并且无论类型构造函数如何,都可以执行相同的操作;因此,我想创建一个lambda

不幸的是,当我在lambda中添加case表达式以对所有三种情况进行模式匹配时,代码的可读性会降低;考虑到类型构造函数的相对同质性,是否有一些内置的习语可以在一个表达式中只提取FilePath,而不必手动进行模式匹配?

我已经尝试将此表达式作为匿名函数传递给watchDir动作的调用:

 wm <- startManager
 sw <- watchDir wm "." (_ -> True) ((_ f t) -> putStrLn f)

但是,可以预见的是,lambda的模式匹配中的don -care值会导致解析错误。

最简单的方法是在类型声明中实际反映可选方案的同质性,而不仅仅是观察它:

data Action = Added | Modified | Removed
data Event = FileEvent Action FilePath UTCTime | OtherEvent ...
f :: Event -> FilePath
f (FileEvent _ path _) = path

一般来说,Haskell没有办法知道所有的构造函数备选项都有相同数量的参数和相同的类型,所以不,你不能抽象替代的选择。

可以说,这个ADT最好被实现为

data Event = Event {
     eventAction :: EventAction
   , eventPath :: FilePath
   , eventTime :: UTCTime
   }
data EventAction = Addition | Modification | Removal

但是这些记录访问器实际上只是普通的函数:

eventTime :: Event -> UTCTime
eventPath :: Event -> FilePath

…当然你自己也可以写出来。但实际上FSNotify包提供了这些精确的函数,所以在您的情况下,问题解决了。

…是吗?

实际上,记录访问器比getter函数更强大:它还允许您修改条目,如

delayEvent :: Event -> Event
delayEvent ev = ev{eventTime = addUTCTime 60 $ eventTime ev}

如果类型实际上不是记录类型,则无法轻松获得该功能。

你可以吗?

这样的

记录在Haskell中从来没有真正令人满意地工作过:它们是特别的语法,与语言的其他部分不太一致,并且它们有作用域问题。在过去的几年里,出现了一种从这些记录访问器转向更奇特的东西的趋势:透镜。

实际上,在较新的库中,您更有可能找到这样的ADT:

import Control.Lens
import Control.Lens.TH
data Event = Event {
    _eventAction :: EventAction
  , _eventPath :: FilePath
  , _eventTime :: UTCTime
  }
makeLenses ''Event

如果你还没有遇到过镜头,这些是它们允许你做的一些例子:

delayEvent :: Event -> Event
delayEvent = eventTime %~ addUTCTime 60
syncEventTo :: Event -> Event -> Event
syncEventTo tref = eventTime .~ tref^.eventTime

现在,与记录访问器不同,镜片可以在Haskell中100%实现,而不需要编译器的特殊语法:

data Event = Added    FilePath UTCTime
           | Modified FilePath UTCTime
           | Removed  FilePath UTCTime
           deriving (Eq, Show)
eventTime :: Lens' Event UTCTime
eventTime = lens get_t set_t
 where get_t (Added    _ t) = t
       get_t (Modified _ t) = t
       get_t (Removed  _ t) = t
       set_t (Added    p _) t = Added p t
       set_t (Modified p _) t = Modified p t
       set_t (Removed  p _) t = Removed p t

最新更新