Хаскель и Штат - PullRequest
       24

Хаскель и Штат

15 голосов
/ 15 октября 2010

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

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

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

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

Ответы [ 5 ]

18 голосов
/ 15 октября 2010

Я собираюсь ответить на ваш второй вопрос первым.На самом деле существует много способов обработки изменяемого состояния в 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

Теперь любые функции, которым требуется PRNG, могут запускаться в монаде Random для запроса их по мере необходимости.Вам просто нужно указать начальное состояние и вычисление.

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

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

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

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

randomList :: Seed -> [Int]

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

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

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

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

11 голосов
/ 15 октября 2010

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

4 голосов
/ 14 октября 2011

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

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

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

4 голосов
/ 16 октября 2010

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

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

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

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

2 голосов
/ 15 октября 2010

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

{-# 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

Удачи.

...