Как сопоставить жесткие типы в экземпляре класса типов?
Вопрос
Я подумал, что попробую смоделировать численное интегрирование векторных величин различной размерности, и решил, что классы типов — это то, что мне нужно.Мне нужно было что-то, чтобы определить разницу между двумя значениями и масштабировать ее с помощью множителя (чтобы получить производную), а также иметь возможность использовать функцию расстояния.
На данный момент у меня есть:
class Integratable a where
difference :: a -> a -> a
scale :: Num b => a -> b -> a
distance :: Num b => a -> a -> b
data Num a => Vector a = Vector1D a | Vector2D a a
instance Num a => Integratable (Vector a) where
difference (Vector1D x1) (Vector1D x2) = Vector1D (x1 - x2)
scale (Vector1D x) m = Vector1D (x * m)
distance (Vector1D x1) (Vector1D x2) = x1 - x2
difference (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 - x2) (y1 - y2)
scale (Vector2D x y) m = Vector2D (x * m) (y * m)
distance (Vector2D x1 y1) (Vector2D x2 y2) = sqrt((x1-x2)*(x1-x2)
+ (y1-y2)*(y1-y2))
К сожалению, здесь есть пара проблем, которые я не понял, как решить.Во-первых, scale
функция выдает ошибки.GHC не может этого сказать m
и x
совместимы, поскольку жесткое ограничение типа Num
дается в экземпляре в одном случае, а в Vector
напишите в другом случае...Есть ли способ указать это x
и m
одного типа?
(На самом деле я понимаю, что даже если x
и m
оба Num
, они могут быть не такой же Num
.Как я могу это указать?Если я не могу понять это с помощью Num
, с использованием Double
было бы хорошо, но я бы предпочел оставить это в общих чертах.)
Есть аналогичная проблема с distance
.Попытка указать, что тип возвращаемого значения Num
терпит неудачу, поскольку в определении экземпляра нельзя указать, что a
будет содержать значения, совместимые с b
.
Решение
РЕДАКТИРОВАТЬ: Мне кажется теперь, что статья о функциональные зависимости из HaskellWiki предоставляет ключевую информацию в лучшей форме, которую я могу найти, поэтому я бы предложил прочитать ее вместо моего ответа здесь.Однако я не удаляю остальной контент, поскольку он ясно показывает (надеюсь), почему FD здесь полезны.
Помимо проблемы с группировкой определений, на которую указал Дэйв...
(На самом деле я понимаю, что даже если
x
иm
обаNum
, они могут быть не такой жеNum
.Как я могу это указать?Если я не могу понять это с помощьюNum
, с использованиемDouble
было бы хорошо, но я бы предпочел оставить это в общих чертах.)
Это, на самом деле, основная проблема.Вы не можете умножить Integer
по Float
, сказать.По сути, вам нужно x
и m
в масштабе быть одного типа.
Аналогичная проблема возникает и с расстоянием, но с дополнительными сложностями, sqrt
нуждается в Floating
аргумент.Думаю, вам тоже нужно где-то об этом упомянуть.(Скорее всего, на экземпляре, я думаю).
РЕДАКТИРОВАТЬ: ОК, поскольку sqrt
работает только на Floating
значения, вы можете создать класс типов для тех, которые нужно преобразовать Float
s для Double
s, когда это необходимо.
Другая идея предполагает наличие класса типов Scalable
:
data Vector a = Vector1D a | Vector2D a a deriving (Show)
class Scalable a b | a -> b where
scale :: a -> b -> a
instance (Num a) => Scalable (Vector a) a where
scale (Vector1D x) m = (Vector1D (x * m))
scale (Vector2D x y) m = (Vector2D (x * m) (y * m))
При этом используется так называемая функциональная зависимость в определении Scalable
.Фактически, пытаясь запомнить синтаксис этого, я нашел эта ссылка...Поэтому я думаю, вам следует игнорировать мою неудовлетворительную попытку помочь и прочитать там качественную информацию.;-)
Я думаю, вы сможете использовать это для решения своей первоначальной проблемы.
Другие советы
Я думаю, чтобы исправить вторую ошибку, вам нужно изменить порядок определений в объявлении экземпляра.Сначала имеем два уравнения для difference
, то уравнения для scale
, то оба для distance
.