我想了解下面的表达式。它会将字符列表['a','b','c']
转换为字符串列表["a", "b", "c"]
liftM (:[]) "abc"
这是怎么发生的?
答案 0 :(得分:12)
机器人猴头操作员(:[])
只是列表中(:)
的{{3}}和空列表[]
,即(:[])
等同于{ {1}};反过来也可以使用列表语法编写(\x -> x:[])
。
以这种方式改写,我们有
(\x -> [x])
字符串文字liftM (\x -> [x]) "abc"
也只是section的语法糖,所以我们可以反过来重写上面的
"abc"
character list ['a', 'b', 'c']
,给予
liftM (\x -> [x]) ['a', 'b', 'c']
fmap (\x -> [x]) ['a', 'b', 'c']
Functor
实例设置[]
,提供
fmap = map
减少到
map (\x -> [x]) ['a', 'b', 'c']
或者,回到字符串表示法
[['a'], ['b'], ['c']]
Q.e.d。
答案 1 :(得分:10)
函数liftM
转换一个函数,该函数接受输入并产生输出到一个函数,该函数在一些monad中输入并在同一个monad中产生输出。让我们看看它的定义:
liftM :: Monad m => (a -> b) -> m a -> m b
liftM f mx = mx >>= \x -> return (f x)
Haskell中的字符串是字符列表(type String = [Char]
),所以
"abc" = ['a', 'b', 'c'] :: [Char]
从您的应用程序编译器推断a = Char
,b = [Char]
,m a = [Char]
,m = []
。所以m b = [[Char]] = [String]
。列表是return x = [x]
和(>>=) = concatMap
的monad。因此,如果我们专注于上面的定义,我们得到:
liftM f mx = concatMap (\x -> [f x]) mx
如果我们应用我们得到的论据:
concatMap (\x -> [[x]]) ['a', 'b', 'c'] =
concat $ map (\x -> [[x]]) ['a', 'b', 'c'] =
concat $ [[['a']], [['b']], [['c']]] =
[['a'], ['b'], ['c']] =
["a", "b", "c"]
答案 2 :(得分:9)
liftM
相当于fmap
,仅适用于monad。 (:[])
使用(:)
创建一个生成一个元素列表的函数。就像(+2)
是一种写作(\x -> x + 2)
的简洁方式一样,(:[])
相当于(\x -> x : [])
或(\x -> [x])
。
然后,你的表达可能写成:
fmap (\x -> [x]) "abc"
liftM
的存在反映了这样一个事实:任何合法的Monad
都可以通过Functor
制作成fmap f m = m >>= \x -> return (f x)
。您始终可以将liftM
替换为fmap
,因此使用它的唯一理由是:
如果您已经有fmap
个实例(并且不想使用Monad
GHC扩展名),则免费定义DeriveFunctor
,
一个完全可选的样式选择(如果你写的是明显的monadic代码,并认为liftM
看起来比fmap
好。)
答案 3 :(得分:7)
liftM
定义为:
liftM f m = m >>= \x -> return (f x)
我们将liftM
与列表(字符)一起使用,因此我们需要查看Monad
的列表实例,以了解>>=
和return
的位置定义:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
因此
liftM f xs = xs >>= \x -> return (f x)
= concat (map (\x -> [f x]) xs)
外侧的concat
和内侧的[ ]
相互抵消,所以
liftM f xs = map (\x -> f x) xs
= map f xs
换句话说,列表monad中的liftM
只是map
。
map (:[]) ['a', 'b', 'c'] = [(: []) 'a', (: []) 'b', (: []) 'c']
= ['a' : [], 'b' : [], 'c' : []]
= [['a'], ['b'], ['c']]
= ["a","b","c"]
因为字符串实际上只是一个字符列表。