Ну, я думаю, что лучший способ объяснить это просто написать код.
Прежде всего, вы хотели бы скрыть внутреннюю работу монады, в которой вы сейчас работаете. Мы сделаем это с псевдонимом типа, но есть более мощные способы, см. Эту главу от Real World Haskell.
type PersonManagement = State Int
Причина этого заключается в том в случае, если вы будете добавлять больше вещей PersonManagement позже и его хорошая практика, чтобы использовать окно абстракции черный.
Вместе с определением PersonManagement вы должны разоблачить примитивные операции, которые определяют эту монаду. В вашем случае у нас есть только функция галочки, которая выглядит почти так же, но с более четкой подписью и более наводящим на размышления именем.
generatePersonId :: PersonManagement Int
generatePersonId = do
n <- get
put (n+1)
return n
Теперь все вышеперечисленное должно находиться в отдельном модуле. Помимо этого, мы можем определить более сложные операции, как создание нового человека:
createPerson :: String -> PersonManagement Person
createPerson name = do
id <- generatePersonId
return $ Person id name
Теперь вы, наверное, поняли, что PersonManagement тип вычислений, или процесс, который инкапсулирует логику для работы с людьми и PersonManagement Person
- это вычисление, из которого мы получаем объект человека. Это очень приятно, но как мы на самом деле получаем людей, которых мы только что создали, и что-то с ними делать, например, печатать их данные на консоли. Ну, нам нужен метод «run», который запускает наш процесс и дает нам результат.
runPersonManagement :: PersonManagement a -> a
runPersonManagement m = evalState m startState
runPersonManagement работает монады и получает конечный результат, выполняя все побочные эффекты в фоновом режиме (в вашем случае, помечая состояние Int). Это использует evalState от государственной монады, и он также должен находиться в описанном выше модуле, поскольку он знает о внутренней работе монады. Я предположил, что вы всегда хотите запустить идентификатор человека из фиксированного значения, идентифицированного startState.
Так, например, если мы хотим создать два человека, и распечатать их на консоль, программа будет что-то вроде:
work :: PersonManagement (Person, Person)
work = do
john <- createPerson "John"
steve <- createPerson "Steve"
return (john, steve)
main = do
let (john, steve) = runPersonManagement work
putStrLn $ show john
putStrLn $ show steve
Выход:
Person {id = 0, name = "John"}
Person {id = 1, name = "Steve"}
Поскольку PersonManagement является полноценным monad, вы также можете использовать общие функции от Control.Monad, например. Предположим, вы хотите создать список лиц из списка имен. Ну, это только функция карты, снятая в области монадов - это называется mapM.
createFromNames :: [String] -> PersonManagement [Person]
createFromNames names = mapM createPerson names
Использование:
runPersonManagement $ createFromNames ["Alice", "Bob", "Mike"] =>
[
Person {id = 0, name = "Alice"},
Person {id = 1, name = "Bob"},
Person {id = 2, name = "Mike"}
]
И примеры можно продолжать.
Чтобы ответить на один из ваших вопросов - вы работаете в монаде PersonManagement только тогда, когда вам нужны услуги, предоставляемые этой монадой - в этом случае функция generatePersonId или вам нужны функции, которые, в свою очередь, требуют примитивов монады, таких как work
, которые нуждаются в функцию createPerson
, которая, в свою очередь, должна запускаться внутри монады PersonManagement, потому что ей нужен счетчик с самонастраивающимся счетчиком. Если у вас есть, например, функция, которая проверяет, имеют ли два человека одни и те же данные, вам не нужно работать внутри монады PersonManagement, и она должна быть нормальной, чистой функцией типа Person -> Person -> Bool
.
Чтобы понять, как работать с монадами, вам просто нужно пройти множество примеров. Real World Haskell - отличное начало, и поэтому Learn you a Haskell.
Вы также должны изучить некоторые библиотеки, в которых используются монады, чтобы увидеть, как они созданы и как люди используют их. Один замечательный пример - синтаксические анализаторы, и parsec - отличное место для начала.
Кроме того, этот paper П. Вадлер предоставляет несколько очень приятных примеров, и, конечно же, есть еще много ресурсов, которые готовы быть обнаружены.
Вы должны рассмотреть соответствие тип данных более правильно с вашей проблемой, как для экземпляр карты. – Sarah
Возможно, вы захотите изучить [этот недавний вопрос о узлах дерева меток] (http://stackoverflow.com/questions/12658443/how-to-decorate-a-tree-in-haskell/12658639). –