Haskell是否提供了针对许多可能的数据构造函数进行模式匹配的习惯用法?
在Haskell项目中,我正在处理包中的Haskell是否提供了针对许多可能的数据构造函数进行模式匹配的习惯用法?,haskell,lambda,pattern-matching,Haskell,Lambda,Pattern Matching,在Haskell项目中,我正在处理包中的事件数据类型。事件的构造函数都是: Added FilePath UTCTime Modified FilePath UTCTime Removed FilePath UTCTime 在我的代码中,我只感兴趣的是从事件中提取文件路径,并执行相同的操作,而不考虑构造函数的类型;正因为如此,我很想做一只羔羊 不幸的是,当我将case表达式放入lambda以匹配所有三种情况时,代码的可读性会降低;考虑到类型构造函数的相对同质性,是否有一些内置习惯用法可以在一个
事件数据类型。事件的构造函数都是:
Added FilePath UTCTime
Modified FilePath UTCTime
Removed FilePath UTCTime
在我的代码中,我只感兴趣的是从事件中提取文件路径
,并执行相同的操作,而不考虑构造函数的类型;正因为如此,我很想做一只羔羊
不幸的是,当我将case
表达式放入lambda以匹配所有三种情况时,代码的可读性会降低;考虑到类型构造函数的相对同质性,是否有一些内置习惯用法可以在一个表达式中只提取文件路径
,而不必手动进行模式匹配
我已尝试在调用watchDir
操作时将此表达式作为匿名函数传递:
wm <- startManager
sw <- watchDir wm "." (\_ -> True) (\(_ f t) -> putStrLn f)
wm 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
现在,与记录访问器不同,Lens可以在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
为什么不使用此模块提供的eventTime
/eventPath
访问器功能呢?这实际上是一个很好的解决方案,解决了我的问题。我在看文件的时候忽略了它。我将把这个问题留一段时间,因为我想看看是否有一个通用的等价物。我认为有一个论点是,应该只有一个构造函数,Event FilePath UTCTime EventType
,带有data EventType=Added | Modified | Removed
@chepner,我对这个库一无所知,但是这个选择可能有性能方面的原因。我可以问你为什么接受这个答案吗?您是否决定修改库以满足您的需要?如果你的问题是在一般情况下解决这个问题,那么我建议你改写它来澄清这一点。让问题问A,让被接受的答案回答B是没有意义的。文件事件不仅仅是众多事件中的一个;它是一个单一的和类型,可以用newtype MyEvent=Event(Action,FilePath,UTCTime)
包装以提高效率。是的,如果只有文件事件,则使用新类型更好。