Próbowałem owinąć głowę wokół koncepcji monad i byłem eksperymentuje z poniższym przykładzie:i IO Monady
Mam typu danych Editor
reprezentującą stan tekstu dokument i niektóre funkcje, które na nim działają.
data Editor = Editor {
lines :: [Line], -- editor contents are kept line by line
lineCount :: Int, -- holds length lines at all times
caret :: Caret -- the current caret position
-- ... some more definitions
} deriving (Show)
-- get the line at the given position (first line is at 0)
lineAt :: Editor -> Int -> Line
lineAt ed n = ls !! n
where
ls = lines ed
-- get the line that the caret is currently on
currentLine :: Editor -> Line
currentLine ed = lineAt ed $ currentY ed
-- move the caret horizontally by the specified amount of characters (can not
-- go beyond the current line)
moveHorizontally :: Editor -> Int -> Editor
moveHorizontally ed n = ed { caret = newPos }
where
Caret x y = caret ed
l = currentLine ed
mx = fromIntegral (L.length l - 1)
newX = clamp 0 mx (x+n)
newPos = Caret newX y
-- ... and lots more functions to work with an Editor
Wszystkie te funkcje działają na Editor
, a wielu z nich powróci nowy Editor
(gdzie daszek został przeniesiony lub jakiś tekst został zmieniony), więc pomyślałem, że może to być dobry stosowanie State
monada i ja ponownie napisany najbardziej Editor
działanie funkcji do teraz wyglądać następująco:
lineAt' :: Int -> State Editor Line
lineAt' n = state $ \ed -> (lines ed !! n, ed)
currentLine' :: State Editor Line
currentLine' = do
y <- currentY'
lineAt' y
moveHorizontally' :: Int -> State Editor()
moveHorizontally' n = do
(Caret x y) <- gets caret
l <- currentLine'
let mx = fromIntegral (L.length l - 1)
let newX = clamp 0 mx (x+n)
modify (\ed -> ed { caret = Caret newX y })
moveHorizontally' :: Int -> State Editor()
moveHorizontally' n = do
(Caret x y) <- gets caret
l <- currentLine'
let mx = fromIntegral (L.length l - 1)
let newX = clamp 0 mx (x+n)
modify (\ed -> ed { caret = Caret newX y })
to dość niesamowite, ponieważ pozwala mi komponować czynności edycyjnych bardzo łatwo w ciągu do
-notation.
Jednak obecnie staram się to wykorzystać w ramach rzeczywistej aplikacji. Powiedzmy, że chcę użyć tego Editor
w aplikacji, która wykonuje niektóre IO. Załóżmy, że chcę manipulować instancją Editor
za każdym razem, gdy użytkownik naciśnie klawisz l
na klawiaturze.
musiałbym mieć inny State
monady reprezentujący ogólny stan aplikacji, która posiada instancję Editor
i sortowania zdarzenia pętli, która używa IO
monady odczytu z klawiaturą i wywołuje moveHorizontally'
zmodyfikować obecną AppState modyfikując jego Editor
.
Przeczytałem trochę na ten temat i wydaje mi się, że muszę użyć Monad Transformers, aby zbudować stos monad z IO na dole. Nigdy wcześniej nie używałem Monad Transformers i nie wiem, co robić dalej? Dowiedziałem się też, że monada State
implementuje już pewną funkcjonalność (wydaje się, że jest to szczególny przypadek Monad Transformer?), Ale jestem zdezorientowany, jak tego użyć?
„Wszystkie te funkcje działają na edytorze, a wielu z nich zwraca nowy edytor (gdzie przeniesiono karetkę lub jakiś tekst został zmieniony) "- To dobrze! Jest bardzo mało prawdopodobne, aby zwykła pętla IO wykorzystująca twoje oryginalne czyste funkcje spowodowała utworzenie nowego edytora w pamięci.GHC skopiuje strukturę tylko wtedy, gdy zajdzie taka potrzeba, i zaktualizuje ją, jeśli stare odwołanie nie jest używane. Nie musisz tutaj używać transformatorów, a twój kod będzie bez nich wyraźniejszy. – thumphries
@ DeX3 FYI, przesłanie * samowystarczalnego * posta ułatwia ludziom pisanie kodu, aby odpowiedzieć na twoje pytanie. – gallais