2012-04-28 2 views
1

Я хотел бы определить несколько версий вещи, но с разными типами, чтобы повысить безопасность типов в моей программе. Например, у меня есть несколько типов бивариантных значений, которые я хочу быть экземплярами Num, но все должны быть разных типов. Так что я сделал, создавая newtype с одной переменной типа и объявляя новые типы на основе этого. Тем не менее, я нахожу это немного раздражающим, что теперь мне приходится использовать оба конструктора все время. Есть ли способ обойти это?Haskell: newtype для синонимов, безопасных по типу: любой способ использования двух конструкторов?

{-# LANGUAGE GeneralizedNewtypeDeriving #-} 

newtype Bivar t = Bivar (t,t) deriving (Show, Eq) 

instance (Num t) => Num (Bivar t) where 
    (+) (Bivar (x1,y1)) (Bivar (x2,y2)) = Bivar (x1+x2, y1+y2) 
    (-) (Bivar (x1,y1)) (Bivar (x2,y2)) = Bivar (x1-x2, y1-y2) 
    (*) (Bivar (x1,y1)) (Bivar (x2,y2)) = Bivar (x1*x2, y1*y2) 
    abs (Bivar (x1,y1)) = Bivar (abs x1, abs y1) 
    fromInteger i = Bivar (fromInteger i, fromInteger i) 
    signum (Bivar (x1,y1)) = Bivar (signum x1, signum y1) 

newtype BivarNode = BivarNode (Bivar Int) deriving (Show, Eq, Num) 
newtype BivarVal = BivarVal (Bivar Double) deriving (Show, Eq, Num) 
newtype HBivarVal = HBivarVal (Bivar Double) deriving (Show, Eq, Num) 

-- This is annoying: 
a1 = BivarVal (Bivar (1.0, 2.0)) 
a2 = HBivarVal (Bivar (1.0, 2.0)) 
b = BivarNode (Bivar (1,2)) 

-- is there a way so that I can write it this way? 
aa1 = BivarVal (1.0, 2.0) 
aa2 = HBivarVal (1.0, 2.0) 
bb = BivarNode (1,2) 

спасибо!

EDIT:

Чтобы расширить на оригинальный вопрос, я хотел бы также использовать имена типов на шаблон совпадает, то вдоль линий

myFunction :: HBivarVal -> Double 
myFunction (HBivarVal (Bivar (x,y))) = x 

что тоже возможно?

+6

Для вашего второго вопроса: используйте шаблоны просмотра. –

+0

@ ThomasM.DuBuisson Спасибо, проверим его – Paul

+1

Новинка, обертывающая пару, действительно ничего не дает вам (хотя GHC оптимизирует простую оболочку newtype, все шаблоны должны по-прежнему использовать пару), поэтому 'data Bivar t = Bivar tt' эквивалентен и немного меньше нажатий клавиш ... –

ответ

2

Почему бы не сделать все из них NewType, даже Bivar?

instance (Num a, Num b) => Num (a, b) where 
    (a, b) + (a', b') = (a+a', b+b') 
    (a, b) * (a', b') = (a*a', b*b') 
    (a, b) - (a', b') = (a-a', b-b') 
    fromInteger i = (fromInteger i, fromInteger i) 
    abs (a, b) = (abs a, abs b) 
    signum (a, b) = (signum a, signum b) 

newtype Bivar t = Bivar  (t  , t ) deriving (Show, Eq, Num) 
newtype BivarNode = BivarNode (Int , Int ) deriving (Show, Eq, Num) 
newtype BivarVal = BivarVal (Double, Double) deriving (Show, Eq, Num) 
newtype HBivarVal = HBivarVal (Double, Double) deriving (Show, Eq, Num) 
+1

Если вы не хотите добавлять экземпляр-сирота для 'Num (a, b)', вы можете определить вместо этого данные типа data-data (a-local-helper-data) 'a pair a b = a: * b'. Пользователи модуля даже не должны знать. – luqui

+0

Нуждается в расширении компилятора 'GeneralizedNewtypeDeriving', я думаю. – luqui

+0

Спасибо, я думаю, что это был ответ, который я искал. Я думал, что мне нужен тип, чтобы объявить экземпляр Num. – Paul

1

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

bivarVal = BivarVal . Bivar 
hBivarVal = HBivarVal . Bivar 
bivarNode = BivarNode . Bivar 
+0

Спасибо! Есть ли способ использовать что-то подобное в сопоставлении с образцом, чтобы получить исходные значения? – Paul

+0

Нет, по крайней мере, не так, как написано: вы можете сопоставлять шаблон только конструкторам, а не произвольным функциям. – huon

+0

Да, я знаю. Я мог бы объединить функции деконструирования, чтобы получить значение, но afaik, который все еще не помогает с совпадением шаблонов ... – Paul

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