Задание ограничений класса в конструкторах значений - PullRequest
10 голосов
/ 27 января 2011

Есть ли способ определить ограничение класса для параметра конструктора значения?

Примерно так:

data Point2D = (Num a) => Point a a

чтобы Point мог принимать любые аргументы, если они принадлежат классу Num?

Ответы [ 4 ]

11 голосов
/ 27 января 2011

Вы можете использовать ExistentialQuantification или GADTs, но ни один из них не будет делать то, что вы хотите.Вы никогда не сможете выполнять арифметику с двумя значениями Point2D.Все, что вы знаете о содержимом, это то, что они какой-то экземпляр Num.Вы говорите компилятору отказаться от всей остальной информации о них.Это означает, что вы говорите компилятору отказаться от любой информации, которая может иметь, что конкретная пара значений Point2D содержит один и тот же тип.А без этой информации вы не сможете выполнять арифметику значений из двух Point2D вместе.

Это почти наверняка не то, что вы хотите.Например, вы не можете написать функцию distance.Какое возможное использование вы могли бы использовать для такого ограниченного типа?Все, что вы можете с ними сделать, - это преобразовать их содержимое в String.

Редактировать:

Я думаю, что вижу, что вы пытаетесь сделать.Вы просто хотите убедиться, что все в Point2D является числом.Я не думаю, что вы действительно хотите стереть тип.

В этом случае, я бы пошел с версией GADT, с одним действительно важным изменением:

{-# LANGUAGE GADTs #-}
data Point2D a where
    Point :: (Num a) => a -> a -> Point2D a

Конечный результат этогов том, что вы можете использовать конструктор Point только с двумя значениями одного и того же экземпляра Num, но вы не потеряете тип.Кроме того, благодаря использованию GADTs, сопоставление с образцом в конструкторе Point восстанавливает для вас контекст Num, чего, как правило, и следовало ожидать.

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

4 голосов
/ 27 января 2011

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

Обычно, дженерики как в type Foo a = (a, a) означают

для всех типов a, Foo a состоит из двух a

Однако в вашем примере нужно сформулировать это по-другому:

Для некоторых типов a, Point2D состоит из двух a

или

Существует тип a, из которого Point2D состоит из

Таким образом, универсальный тип не является универсальным (для всех типов ...), но экзистенциальным (существует некоторый тип ...) Под GHC мы можем разрешить это через расширение

{-# ExistentialQuantification #-}

как описано в этой статье по теме . Ваш код, в конце концов,

data Point2D = forall a . Num a => Point a a
2 голосов
/ 27 января 2011

Конечно!

Это должно делать то, что вы хотите:

{-# LANGUAGE GADTs #-}

data Point2D a where
    Point :: Num a => a -> a -> Point2D a

p :: Num a => a -> a -> Point2D a
p = Point

sumP :: Point2D a -> Point2D a -> a
sumP (Point a b) (Point c d) = a + b + c + d

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

1 голос
/ 27 января 2011
{-# LANGUAGE GADTs #-}
data Point2D where
    Point :: (Num a) => a -> a -> Point2D

Это обобщенный алгебраический тип данных .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...