Как создать тип, ограниченный в определенном диапазоне

Я хотел бы создать новый интегральный тип, ограниченный определенным диапазоном. Я пробовал:

data PitchClass = PC Int deriving (Ord, Eq, Show)

instance Bounded PitchClass where
  minBound = PC 0
  maxBound = PC 11

Однако я хочу, чтобы что-то не получилось, если что-то вроде

PC 12

или

PC (-1)

.

Является общим подходом к ситуации, в которой вы хотите установить ограничения на создание новых типов, в которых конструкторы значения не экспортируются из модуля, а скорее функции, возвращающие экземпляры типа и выполняющие проверки ограничений, экспортируются

Ответы

Ответ 1

Да, не нужно экспортировать конструктор данных из модуля.

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

Ответ 2

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

data PitchClass = A | Bb | B | C | Db | D | Eb | E | F | Gb | G | Ab
    deriving (Eq, Ord, Bounded, Show, Read)

Есть полдюжины разных хаков, которые вы можете попробовать отсюда, чтобы сделать его более удобным по-разному; например, вы можете получить Enum, чтобы получить toEnum . fromEnum = idtoEnum (-1) = {- an exception -}), или вы можете написать собственный экземпляр Integral, чтобы получить 0 = A (и ваш выбор поведения для -1).