2017-02-04 1 views
1

Предположим, что у меня есть эти определения данных в Haskell.Как написать общую функцию для любого конструктора данных?

data Point a = Point a a 
data Piece a = One a | Two a 

и я хочу иметь одну функцию, как

place :: Num a => Piece (Point a) -> Point a 
place (Piece x) = x 

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

Решение

Проблема заключалась в мое определение данных (много лет эмпирического программирования мешая здесь ...). Я хотел, чтобы мои данные Piece эффективно представляли собой две разные вещи: кусок, а также какую-то часть, я пытался добиться этого с наследованием, которое было всего лишь плохим подходом. Разделение данных следующим образом разрешило проблему.

data PieceKind = One | Two | ... | n 
data Piece a b = Piece a PieceKind 

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

place :: Num a => Piece (Point a) PieceKind -> Point a 
place (Piece x _) = x 

Кроме того, я также был в состоянии писать функции для определенного вида кусок:

place :: Num a => Piece (Point a) PieceKind -> Point a 
place (Piece _ One) = ... 

и это то, что я действительно хотел.

+0

Является ли ваше намерение заставить его работать как 'place (One x) = x; место (два x) = x'? –

+0

Да, вместо одной функции для каждого конструктора я хочу иметь только одну функцию для всех из них. –

+0

Задайте свой вопрос самостоятельно, указав «Точка». – Jubobs

ответ

2

Используйте запись для общих полого

data Piece a = One { place :: a } | Two { place :: a } 
-- place automatically defined 

Или факторизовать общие данные

data Piece a = Piece Bool a 

place :: Piece a -> a 
place (Piece _ x) = x 

-- optionally: 
pattern One x = Piece False x 
pattern Two x = Piece True x 

В общем случае вместо Bool вы должны использовать пользовательский тип суммы, чтобы выразить остальное факторизации. Например.

data T a = A Int a | B Bool a 

становится

data T a = T (Either Int Bool) a 
-- optionally: 
pattern A n x = T (Left n) x 
pattern B b x = T (Right b) x 

Template Haskell также может решить эту проблему, но это излишеством, IMO.

+0

Все еще не совсем то, что я ожидал, ваше предложение в порядке, если у вас есть несколько конструкторов, но что, если у вас есть 10, и все они имеют общую функцию, то с вашим решением мне придется повторять '{ место :: a} '10 раз. Должен быть лучший способ. –

+0

@ DovydasRupshys Нет, если вы правильно оцениваете свои данные, как предложено ниже. – ThreeFx

+0

@ DovydasRupšys Я не думаю, что есть намного более простой способ. Вы можете автогенерировать нужную функцию с помощью шаблона Haskell, который может генерировать код для вас, как в макросистеме, но для его настройки потребуется некоторое внимание. Если у вас очень много типов/конструкторов, это может стоить того, но в противном случае требуется слишком много усилий. – chi