2015-08-01 1 views
7

Próbuję zrozumieć poniższe wyrażenie. Przekształca ono listę znaków ['a','b','c'] do listy ciągów ["a", "b", "c"]jak działa liftM (: [])

liftM (:[]) "abc" 

Jak to się stało?

Odpowiedz

10

Funkcja liftM zamienia funkcję, która pobiera dane wejściowe i wytwarza wyjście do funkcji, która pobiera dane wejściowe w niektórych monadach i generuje wyjście w tej samej monadie. Spójrzmy na jego definicji:

liftM :: Monad m => (a -> b) -> m a -> m b 
liftM f mx = mx >>= \x -> return (f x) 

Struny w Haskell są wykazy znaków (type String = [Char]), więc

"abc" = ['a', 'b', 'c'] :: [Char] 

ze swojego kompilatora aplikacji wywodzi a = Char, b = [Char], m a = [Char], m = []. Tak więc m b = [[Char]] = [String]. Lista to monada, gdzie return x = [x] i (>>=) = concatMap. Więc jeśli specjalizujemy powyżej definicji otrzymujemy:

liftM f mx = concatMap (\x -> [f x]) mx 

A jeśli zastosujemy argumentów otrzymujemy:

concatMap (\x -> [[x]]) ['a', 'b', 'c'] = 
concat $ map (\x -> [[x]]) ['a', 'b', 'c'] = 
concat $ [[['a']], [['b']], [['c']]] = 
[['a'], ['b'], ['c']] = 
["a", "b", "c"] 
+0

oraz 'concatMap (\ x -> [f x])' to 'map f'. –

9

liftM jest równoważna fmap, wyspecjalizowaną tylko do monad. (:[]) używa (:) do utworzenia funkcji, która tworzy listy jednego elementu. Podobnie jak (+2) jest kompaktowym sposobem pisania (\x -> x + 2), (:[]) jest odpowiednikiem (\x -> x : []) lub (\x -> [x]).

wyrażenia, a następnie może być zapisany jako:

fmap (\x -> [x]) "abc" 

Istnienie liftM odzwierciedla fakt, że wszelkie uzasadnione Monad może być wykonany w Functor wykonując fmap f m = m >>= \x -> return (f x). Zawsze można zastąpić liftM przez fmap, więc jedynym powodów, aby go używać to:

  • zdefiniować fmap za darmo, jeśli masz już instancję Monad (i nie chcą korzystać z DeriveFunctor GHC rozszerzenie) i

  • całkowicie opcjonalny wybór stylu (jeśli piszesz wyraźnie monadyczny kod i uważasz, że liftM wygląda lepiej niż fmap).

+0

whow, dziękuję! gdzie mogę dowiedzieć się więcej o tym wszystkim? tak, że mogę też z łatwością używać terminów takich jak "istnienie liftM odzwierciedla fakt, że każdy legalny Monad może zostać przekształcony w Functor"? :) – akonsu

+0

@akonsu Przez "uprawomocniony" rozumiem taki, który wynika z praw monady. Krótkie wprowadzenie do nich to [druga połowa rozdziału "Understanding monads" w Wikibooks] (https://en.wikibooks.org/wiki/Haskell/Understanding_monads#Monad_Laws). Jedno, o czym nie wspomina to to, że dla dowolnego konstruktora typów wszystkie implementacje 'fmap', które są zgodne z prawem [first * functor *] (https: //en.wikibooks.org/wiki/Haskell/The_Functor_class # The_functor_laws), 'fmap id = id', są koniecznie (i automatycznie) równoważne. – duplode

7

liftM jest zdefiniowany jako:

liftM f m = m >>= \x -> return (f x) 

Używamy liftM z listy (znaków), więc musimy patrzeć na przykład katalogowej Monad zobaczyć jak >>= i return są zdefiniowane:

instance Monad [] where 
    return x = [x] 
    xs >>= f = concat (map f xs) 

Zatem

liftM f xs = xs >>= \x -> return (f x) 
      = concat (map (\x -> [f x]) xs) 

concat na zewnątrz i od wewnątrz [ ] znoszą się nawzajem, więc

liftM f xs = map (\x -> f x) xs 
      = map f xs 

Innymi słowy, liftM na liście monady jest po prostu map.

map (:[]) ['a', 'b', 'c'] = [(: []) 'a', (: []) 'b', (: []) 'c'] 
          = ['a' : [], 'b' : [], 'c' : []] 
          = [['a'], ['b'], ['c']] 
          = ["a","b","c"] 

ponieważ łańcuch jest tak naprawdę tylko listą znaków.

12

Głowica operatora robota małpa (:[]) jest tylko section z listy wad (:) i pustym, [] tj (:[]) odpowiada (\x -> x:[]); która z kolei może być również zapisana przy użyciu składni listy jako (\x -> [x]).

Przepisany ten sposób, mamy

liftM (\x -> [x]) "abc" 

Ciąg dosłowne "abc" jest również cukier tylko składnia character list ['a', 'b', 'c'], więc możemy z kolei przepisać powyższe jako

liftM (\x -> [x]) ['a', 'b', 'c'] 

liftM is just fmap from the Dark Days when Functor wasn't a superclass of Monad, dając

fmap (\x -> [x]) ['a', 'b', 'c'] 

The Functor instancja [] zestawów fmap = map, dając

map (\x -> [x]) ['a', 'b', 'c'] 

co zmniejsza do

[['a'], ['b'], ['c']] 

Or, wracając do notacji strun

["a", "b", "c"] 

co było do okazania

+4

+1 dla "robota małpiego operatora" Nigdy nie słyszałem tego określenia i odtąd moje doświadczenie w programowaniu haskell będzie jeszcze bardziej niesamowite! – epsilonhalbe