2010-10-15 4 views
14

Haskell - это чистый функциональный язык программирования.Haskell and State

Мой вопрос: Каковы преимущества и недостатки использования Haskell для решения проблем, связанных с большим количеством состояний, например, программирования графического интерфейса или программирования игр?

Также вторичный вопрос: какие методы должны обрабатывать состояние в функциональном режиме?

Заранее спасибо.

+0

http://www.haskell.org/all_about_monads/html/statemonad.html –

+4

Управление состоянием в функциональном языке включает передачу состояния в функции. Монады упрощают это. – tylermac

+0

@tylermac я никогда не мог понять монады, ну, я не могу сказать, что я глуп, по крайней мере, я BS в CS, но монады ... вы знаете хорошие уроки? – Andrey

ответ

17

Я собираюсь ответить на ваш второй вопрос. На самом деле существует множество способов обработки изменчивого состояния в Haskell (и других языках FP). Прежде всего, Haskell поддерживает изменчивое состояние в IO, через конструкции IORef и mvar. Используя их, они будут хорошо знакомы программистам с императивных языков. Существуют также специализированные версии, такие как STRef и TMVar, а также изменяемые массивы, указатели и различные другие изменяемые данные. Самый большой недостаток состоит в том, что они обычно доступны только в IO или более специализированной монаде.

Наиболее распространенный способ моделирования состояния на функциональном языке явно передает состояние как аргумент функции и возвращаемое значение. Например:

randomGen :: Seed -> (Int, Seed) 

Здесь randomGen принимает параметр семян и возвращает новое семя. Каждый раз, когда вы его вызываете, вам нужно следить за семенем для следующей итерации. Этот метод всегда доступен для передачи состояния, но он быстро становится утомительным.

Возможно, наиболее распространенным методом Haskell является использование монады для инкапсуляции этого состояния. Мы можем заменить randomGen с этим:

-- a Random monad is simply a Seed value as state 
type Random a = State Seed a 

randomGen2 :: Random Int 
randomGen2 = do 
    seed <- get 
    let (x,seed') = randomGen seed 
    put seed' 
    return x 

Теперь любые функции, которые нуждаются в ГСЧ может работать в случайном порядке монады просить их по мере необходимости. Вам просто нужно предоставить начальное состояние и вычисление.

runRandomComputation :: Random a -> Seed -> a 
runRandomComputation = evalState 

(обратите внимание, что есть функции, которые значительно сокращают определение randomGen2; я выбрал наиболее явную версию).

Если вашему случайному вычислению также необходим доступ к IO, то вы используете версию трансформатора монады государства, StateT.

Особо следует отметить монаду ST, которая по существу обеспечивает механизм для инкапсуляции IO-специфических мутаций в сторону от остальной части IO. Монада ST предоставляет STRefs, которые являются изменяемой ссылкой на данные, а также изменяемыми массивами.Используя ST, можно определить такие вещи, как это:

randomList :: Seed -> [Int] 

где [Int] бесконечный список случайных чисел (это будет цикл в конечном счете, в зависимости от вашего PSRG) от начальной семени вы даете ему.

И наконец, есть Functional Reactive Programming. Вероятно, самыми известными в настоящее время библиотеками для этого являются Yampa и Reactive, но другие заслуживают внимания. Существует несколько подходов к изменяемому состоянию в рамках различных реализаций FRP; из-за моего небольшого использования они часто кажутся похожими на концепцию сигнальной структуры, как в QT или Gtk + (например, добавление слушателей для событий).

Теперь, для первого вопроса. Для меня самым большим преимуществом является то, что изменяемое состояние отделено от другого кода на уровне типа. Это означает, что код не может случайно изменить состояние, если он явно не упоминается в сигнатуре типа. Он также дает очень хороший контроль состояния только для чтения в сравнении с изменчивым состоянием (монада-монада и монада монахов). Мне очень полезно структурировать свой код таким образом, и полезно быть в состоянии сказать только от сигнатуры типа, если функция может быть мутировало состояние неожиданно.

У меня лично нет никаких оговорок относительно использования изменчивого состояния в Haskell. Самая большая трудность заключается в том, что было бы утомительно добавлять состояние к тому, что раньше не требовалось, но то же самое было бы утомительно в других языках, которые я использовал для подобных задач (C#, Python).

+0

Что такое 'Seed'? Я попытался определить его как int в верхней части моего файла ('type Seed = Int'), но получить ошибку' No instance for (Show (Random Int)) '(я изначально использовал теорию вашего кода, работа, поэтому попробовал скопировать все это и получил эту ошибку). –

+0

Это больше псевдокода, чем фактическая реализация. «Семя» представляет собой абстрактный тип, представляющий данные, зависящие от состояния PRNG. Я использовал PNRG, потому что это хорошо известный пример класса алгоритмов с сохранением состояния. Если вам нужен генератор случайных чисел, я бы рекомендовал такой пакет, как 'mwc-random' или' mersenne-random'. –

+0

Спасибо за ответ. Я пытаюсь по-настоящему понять moads, а не использовать PRNG, но я думаю, что это действительно хороший пример. Я полностью застрял. Я задал один вопрос об этом пару часов назад, но теперь я еще больше смущен! http://stackoverflow.com/questions/23595363/simple-haskell-monad-random-number/ –

11

Хотя я не сомневаюсь, что люди будут отвечать «использовать государственную монаду», я хотел бы указать еще один полезный метод: функциональное реактивное программирование (с Ямпой или иначе).

2

Обычно, вы должны использовать Monad Transformer с StateT и IO, потому что для отображения (GUI) требуется IO, чтобы ответить, однако, как только вы определили свой Monad Transformer в newtype, вы хотели бы сделать подписи логики игры только с интерфейсом MonadState, таким образом, у вас все еще есть преимущество изменений, отличных от IO-ness. Код ниже объяснить, что я имею в виду:

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 
import Control.Monad.State 

data GameState = GameState { ... } deriving (Show) 
newtype GameMonad a = GameMonad (StateT GameState IO a) 
         deriving (Monad, MonadState GameState, MonadIO) 

-- This way, now you have a monad with the state of the info 
-- what would you like now is being able to modify the state, without actually 
-- having IO capabilites. 

movePlayerOnState :: (MonadState GameState) m => Position -> m() 
-- In this function we get the state out of the state monad, and then we modify 
-- with a pure function, then put the result back again 

-- Other times you would like to have the GUI API available, requiring the IO monad 
-- for that 
renderGameFromState :: MonadIO m => GameState -> m() 
-- in this method you would use liftIO method to call the GUI API 

Этот код является довольно сложным, если вы не понимаете монады, но мое правило заключается в том, выяснить, что государство Монада для, понять, что Монада Трансформаторы (без необходимость понимания того, как они работают) и как использовать монаду StateT.

Я могу указать вам на проект Sokoban я сделал с другим товарищем по команде, который может быть полезен, он использует Ncurses как GUI, но вы можете получить представление о логике и как нам удалось государства на игре

http://github.com/roman/HaskBan

Удачи.

4

Каковы преимущества и недостатки использования Haskell для решения проблем, связанных с большим количеством состояний, например, программирования графического интерфейса или программирования игр?

Преимущество заключается в том, что, даже если вы не особенно пользуясь чистоты, Haskell просто хороший язык.

Первоклассные функции - не должно быть большой сделкой в ​​2010 году, но это так. Алгебраические типы с совпадением рисунков. Мощная проверка статического типа с помощью вывода типа. Чистый синтаксис. Первоклассный параллелизм, STM и чистый поток без потоков. Хороший компилятор. Тонны библиотек и больше с каждым днем. Активное, полезное сообщество.

Это не большие идеологические решения, такие как чистота или лень. Это просто хорошие идеи.Это то, что может быть у большинства языков, но слишком многие этого не делают.

+0

Не могли бы вы рассказать мне, что подразумевается под первоклассным параллелизмом? –

4

Государственная монада - это худший способ моделирования графического интерфейса или игры в Haskell. Я считаю, что второй вариант - использовать параллелизм в обоих случаях. Однако лучший вариант был упомянут Полом: Функциональное реактивное программирование (FRP).

Лично я защищаю стрелку FRP (AFRP), которая, как я думаю, была впервые реализована как Ямпа, а затем разветвлена ​​как немного более полезная Animas. Тем не менее, Yampa быстро достигает своего предела, поэтому я написал более мощную, более выразительную библиотеку под названием netwire, которая также имеет некоторые концептуальные улучшения по сравнению с предыдущими двумя.

По существу, AFRP является функциональной государственной системой. Он функционирует в этом состоянии, не моделируется как изменяющиеся значения переменных, а мутирующие функции. Это чище и не требует каких-либо императивных программ, таких как государственные монады.

Смежные вопросы