2016-11-03 2 views
4

Это вопрос о стиле Haskell.Полезно ли создать коллекцию универсальных типов функций в Haskell?

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

data History a = History [a] [a] 

Первый стек - это история, скажем, игры. Второй стек хранит состояния, которые были выбиты undo. Так

undo (History (x:xs) redoStack) = History xs (x:redoStack) 
undo history = history -- in case there is nothing to undo 

и

redo (History hStack (x:redoStack)) = History (x:hStack) redoStack 
redo history = history -- in case there is nothing to redo 

Существует также общая операция, которая применяет изменения текущего состояния.

applyAChange change (History (x:xs) _) = History ((change x):x:xs) [] 

Тип applyAChange является

applyAChange :: (a -> a) -> History a -> History a 

я решил определить тип Change:

type Change a = a -> a 

Тогда тип applyAChange становится

applyAChange :: Change a -> Change (History a) 

Это показалось полезным, и я использовал Change типа в другом месте в коде.


При определении Stringэкземпляр я обнаружил, определяя ряд функций с типами, как:

convertASomethingToString :: Something -> String 

Так я определил ToString тип

type ToString a = a -> String 

Это позволило мне напишите предыдущие функции типами

convertASomethingToString :: ToString <Something> 

Все, что кажется приятным и делает код более информативным. Мои вопросы:

  1. Сколько стоит такого рода вещи?
  2. До тех пор, пока это делается, имеет смысл создать модуль, который состоит из этих типов определений типов и импортировать его? Это стандартная вещь?
  3. Есть ли уже широко используемый модуль такого рода с полезными типами?

Спасибо.

+2

В поддержку такого типа рефакторинга ваш тип «Change» является повторным изобретением типа «Endo» в «Data.Monoid»: 'newtype Endo a = Endo {appEndo :: a -> a}' , – chepner

+3

@RussAbbott Я бы поставил «текущее состояние» в отдельный слот типа данных вместо того, чтобы связать его с одним из списков. Естественно предположить, что в любом случае всегда будет «текущее состояние». – danidiaz

+2

@chepner, но 'Endo' на самом деле полезен, потому что' Endo a' имеет экземпляр «Monoid». Менее полезно, возможно, «Endo» является экспоненциальным функтором. С другой стороны, синоним типа «Change», похоже, делает немного больше, чем запутывание. Но это мое мнение о синонимах большинства типов. – dfeuer

ответ

2

Как уже упоминалось ранее, вы можете использовать Endo a newtype от Data.Monoid вместо своего псевдонима типа Change. Иногда может быть неудобно заниматься упаковкой/распаковкой.Но вы можете найти что-то полезное в своем примере Monoid, если вы хотите скомпоновать изменений.

Что касается вашего History Тип данных: общеизвестно ListZipper в мире Haskell. Вы можете прочитать об этой концепции в разных местах. Но в вашем коде вы уже можете использовать существующий пакет для взлома: http://hackage.haskell.org/package/ListZipper

У этого есть свои функции и некоторые другие, которые могут вам пригодиться.

ToString тип псевдонима в основном тип show функция без Show ограничение. Я не знаю, как вы можете преобразовать что-то общее в String без Show типа связанного, но я не могу сказать больше, не глядя на ваш код. Итак, моя точка зрения заключается в том, что вам в основном не нужен псевдоним типа ToString. Лучше использовать стандартные функции и классы классов.

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