2016-02-23 7 views
1

У меня есть параметризованный тип, который я хотел бы, чтобы ограничить к числовому типу, более конкретно Fractional, например:Могу ли я ограничить параметрический полиморфный тип конструктора типов/данных в Haskell?

data Rating a = (Fractional a) => Score a | Unscored deriving (Show, Eq) 

так что пользователь API может определить, какой нецелый типа они могут использовать (Float или Double?), Но внутренний код API, который я пишу, все равно может выполнять арифметические операции над числовым типом. Я не хочу, чтобы это целое число, потому что результаты моих «внутренних операций» могут быть не целыми числами, и я понимаю, что использование Fractional приведет к более точным результатам.

Компиляция выше (в GHCI по крайней мере) дает мне следующую ошибку:

Data constructor `Score' has existential type variables, a context, or a specialised result type 
    Score :: forall a. Fractional a => a -> Rating a 
    (Use ExistentialQuantification or GADTs to allow this) 
In the definition of data constructor `Score' 
In the data declaration for `Rating' 

, который подсказывает мне, что я делаю что-то, что я, вероятно, не хотят продолжать попытки; т. е. мой дизайн - мусор.

Я предполагаю, что я пытаюсь сказать следующее в этом API: «когда вы используете тип рейтинга, его параметр должен быть подклассом Fractional, поэтому я могу выполнить точные арифметические действия». Как я могу это достичь? Или я не в курсе и/или переоснащение?

+0

Уже обсуждался в [этом ответе] (http://stackoverflow.com/questions/12770278/typeclass-constraints-on-data-declarations). –

+0

Да, ты прав. Благодарю. Тем не менее, мне все еще нравятся аспекты API-дизайна моего вопроса, и ответ Твина ниже удовлетворяет соглашению с моим другим решением, которое должно было наложить на него функции, которые не рассматриваются в этом другом вопросе. –

ответ

2

Как говорит ошибка, вы можете сделать это с GADTs:

{-# LANGUAGE GADTs, StandaloneDeriving #-} 

data Rating a where 
    Score :: (Fractional a) => a -> Rating a 
    Unscored :: Rating a 

deriving instance (Show a) => Show (Rating a) 
deriving instance (Eq a) => Eq (Rating a) 

-- No need for a context here! 
halfScore :: Rating a -> Rating a 
halfScore (Score a) = Score (a/2) 
halfScore Unscored = Unscored 

StandaloneDeriving необходимо потому, что GHC не может получить Show и Eq для GADTs иначе.

5

Вы не должны ставить ограничение Fractional на тип данных, а на функции, которые с ним работают. Так

data Rating a = Score a | Unscored deriving (Show, Eq) 

makeSomeRating :: Fractional a => a -> Rating a 
makeSomeRating x = Score (x/2) -- can use Fractional functions here 

doSomethingElseWithRating :: Fractional a => Rating a -> Something 
+0

Я задавался этим вопросом. Моя причина не в этом заключалась в том, что я приложил потребность работать с 'Fractional' типа' Rating', т. Е. Не имеет смысла использовать «Рейтинг», если вы не работаете с какой-либо производной от 'Fractional' (но какой из них используется для пользователя ...). Имеет ли это смысл? Не уверен, изменит ли он ваш ответ или нет ... –

+0

@atc, этот ответ отражает общее мнение сообщества о лучших практиках. Люди много пробовали, и это обычно работает лучше всего. Существуют особые обстоятельства, когда GADT - это путь, но это не один из них. Также обратите внимание, что эта позиция писателя-ответчика в сообществе Haskell намного превышает его рейтинг репутации SO ;-) – dfeuer

+0

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

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

  • Нет связанных вопросов^_^