【问题标题】:Does Haskell provide an idiom for pattern matching against many possible data constructors?Haskell 是否为许多可能的数据构造函数提供模式匹配的习惯用法?
【发布时间】:2016-09-12 18:40:23
【问题描述】:

在处理 Haskell 项目时,我正在处理来自 FSNotify 包的 Event 数据类型。 Event的构造函数都是:

Added FilePath UTCTime
Modified FilePath UTCTime
Removed FilePath UTCTime

在我的代码中,我只对从Event 中提取FilePath 感兴趣并执行相同的操作,而不管类型构造函数如何;因此,我很想制作一个 lambda。

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

我尝试在调用 watchDir 操作时将此表达式作为匿名函数传递:

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

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

【问题讨论】:

  • 为什么不用这个模块提供的eventTime/eventPath访问器函数呢?
  • 这实际上是一个很好的解决方案,并且解决了我的问题。我在阅读文档时忽略了它。我将把这个问题留一会儿,因为我有兴趣看看是否有一个通用的等价物。
  • 我认为应该只有一个构造函数Event FilePath UTCTime EventTypedata EventType = Added | Modified | Removed
  • @chepner,我对这个库一无所知,但这个选择可能有性能原因。
  • 请问您为什么接受这个答案?您是否决定修改库以满足您的需要?如果您的问题是在一般环境中解决该问题,那么我建议您改写它以澄清这一点。问 A 的问题和回答 B 的公认答案是没有意义的。

标签: haskell lambda pattern-matching


【解决方案1】:

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

data Action = Added | Modified | Removed

data Event = FileEvent Action FilePath UTCTime | OtherEvent ...

f :: Event -> FilePath
f (FileEvent _ path _) = path

一般而言,Haskell 无法知道您的所有构造函数替代方案都具有相同数量的参数和相同的类型,所以不,您不能抽象出替代方案的选择。

【讨论】:

  • 文件事件不仅仅是众多事件之一;它是一个单一的 sum 类型,可以用 newtype MyEvent = Event (Action, FilePath, UTCTime) 包装以提高效率。
  • 是的,如果只有文件事件,newtype 更好。
【解决方案2】:

可以说,这个 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 中从来没有真正令人满意地工作过:它们是临时语法,与语言的其他部分不太一致,并且存在范围问题。在过去的几年里,这些记录访问者已经从这些记录访问器转向更花哨的东西:lenses

实际上,在较新的库中,您更有可能找到 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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-04
    • 1970-01-01
    • 2018-06-09
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    相关资源
    最近更新 更多