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?
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?
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"]
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
).
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
@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
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.
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
+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
oraz 'concatMap (\ x -> [f x])' to 'map f'. –