2012-06-10 2 views
10

У меня есть эти типы данных:Haskell: «сколько» типа должны получать функции? и избежать полной «реконструкции»

data PointPlus = PointPlus 
    { coords :: Point 
    , velocity :: Vector 
    } deriving (Eq) 

data BodyGeo = BodyGeo 
    { pointPlus :: PointPlus 
    , size :: Point 
    } deriving (Eq) 

data Body = Body 
    { geo :: BodyGeo 
    , pict :: Color 
    } deriving (Eq) 

Это базовый тип данных для персонажей, врагов, предметы и т.д. в моей игре (ну, я просто два прямоугольника, как игрока и земля прямо сейчас: p).

При нажатии клавиши символы перемещаются вправо, влево или прыгают, изменяя свой velocity. Перемещение выполняется путем добавления velocity к coords. В настоящее время, это записывается следующим образом:

move (PointPlus (x, y) (xi, yi)) = PointPlus (x + xi, y + yi) (xi, yi) 

Я просто принимая PointPlus часть моей Body, а не весь Body, в противном случае было бы:

move (Body (BodyGeo (PointPlus (x, y) (xi, yi)) wh) col) = (Body (BodyGeo (PointPlus (x + xi, y + yi) (xi, yi)) wh) col) 

ли первая версия move лучше ? В любом случае, если move изменяет только PointPlus, должна быть другая функция, которая вызывает ее внутри нового Body. Я объясняю: есть функция update, которая вызывается для обновления состояния игры; он передает текущее состояние игры, одно Body, и возвращает обновленный Body.

update (Body (BodyGeo (PointPlus xy (xi, yi)) wh) pict) = (Body (BodyGeo (move (PointPlus xy (xi, yi))) wh) pict) 

Это щекочет меня. Все сохраняется в пределах Body, за исключением PointPlus. Есть ли способ избежать этой полной «реконструкции» вручную? Как и в:

update body = backInBody $ move $ pointPlus body 

Без того, чтобы определить backInBody, конечно.

ответ

14

Вы ищете «линзы». Существует несколько различных пакетов для линз; here - хорошее резюме из них.

Я понимаю, что линза на типе a данных для некоторого поля b предоставляет две операции: способ получить значение b и способ, чтобы получить новый a с другим значением b. Таким образом, вы просто используете объектив для работы с глубоко вложенным PointPlus.

Комплекты линз обеспечивают полезные функции для работы с объективами, а также способы генерации объективов автоматически (с шаблоном Haskell), которые могут быть очень удобными.

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

+0

Это прекрасно, особенно с автоматической генерацией! Как насчет функции 'move'? Лучше ли это взять все «Тело» или только его часть «PointPlus»? – L01man

+3

@ L01man Я также описываю конкретный пример, очень похожий на ваш в блоге моего на линзах [здесь] (http://www.haskellforall.com/2012/01/haskell-for-mainstream-programmers_28.html) –

+0

Пошаговое объяснение помогло мне разобраться в объективах. – L01man

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