Haskell中优雅访问Monad内记录值的方法
问题泛化
给定函数类型定义:f :: MonadIO m => a -> m B,其中B是自定义数据类型B = B {x, y, z ...},如何从f返回的值中访问x这样的成员?
实际问题
我尝试使用wave包开发应用,但遇到一个简单问题:我希望访问Wave数据类型的waveFileFormat成员。
然而直接执行waveFileFormat $ readWaveFile "file"无法生效,因为readWaveFile "file"实际返回的是MonadIO m => m Wave类型。
尝试过的无效方法
readWaveFile "file" >>= waveFileFormatreadWaveFile "file" >> waveFileFormat
可行但不够优雅的方法
do{wave<-readWaveFile "file"; return $ waveFileFormat wave}
但这种方法无法在do块外使用,且代码冗长冗余,如何优化?
优化方案
方法1:使用<$>运算符(fmap的中缀形式)
这是最简洁的实现方式,所有Monad都属于Functor类型类,<$>可以直接把字段访问函数映射到m Wave这个monadic值上:
waveFileFormat <$> readWaveFile "file"
它和你写的do块功能完全等价,但写法紧凑,且能在do块外直接使用。
方法2:使用liftM
如果你更习惯Monad相关的工具函数,liftM也能达到同样效果,它是专为Monad设计的映射函数:
liftM waveFileFormat $ readWaveFile "file"
liftM f m和f <$> m功能一致,只是前者属于Monad类型类,后者属于Functor类型类,现代Haskell更推荐用<$>,因为它适用范围更广。
无效方法的原因
>>=要求右侧函数是a -> m b类型,但waveFileFormat是Wave -> FileFormat(非monadic),类型不匹配;>>会忽略左侧monad的值,直接执行右侧操作,但waveFileFormat不是monadic操作(不返回m something),类型也不匹配。
内容的提问来源于stack exchange,提问作者Naitik Mundra
相关产品推荐
相关产品推荐

