Существуют ли значения по умолчанию для получателей записей в Haskell? - PullRequest
4 голосов
/ 25 января 2012

Неудивительно, что исключение во время выполнения выдается следующим кодом:

data Necklace = InvalidNecklace |
    Necklace { necklace_id :: Int, meow :: Int, ... }
necklace_id InvalidNecklace

Существует ли какой-то естественный способ определения значения для necklace_id при применении к InvalidNecklace для получения значения вместобросить исключение?

GHC завершается ошибкой с несколькими объявлениями для necklace_id, если я пытаюсь сделать очевидную вещь:

necklace_id InvalidNecklace = -1

Возможно, есть какая-то прагма, которая скажет GHC заменить это логическое объявлениеобъявление?

Я мог бы объявить InvalidNecklace записью, добавив { necklace_id :: Int }, но на самом деле я не могу гарантировать, что она всегда возвращает -1 и, как правило, создает ужасный беспорядок.Я мог бы просто определить:

get_necklace_id InvalidNecklace = -1
get_necklace_id x = necklace_id x

, но это частично противоречит цели записей.

Полагаю, можно создать специальное значение invalidNecklace, написав:

invalidNecklace = Necklace { necklace_id = -1,
     meow = error "meow invalidNecklace accessed", ... }

Есть ли какие-либо недостатки у этого второго подхода?Я, конечно, теряю способность делать meow строгими или распакованными, но, возможно, можно было бы поддерживать отдельные отладочные и оптимизированные версии.Существует ли прагма для локального отключения предупреждений для частично инициализированных записей?

Ответы [ 2 ]

8 голосов
/ 25 января 2012

(ОБНОВЛЕНО НИЖЕ)

Как вы обнаружили, метод получения, определенный в объявлении Necklace, не может быть определен далее. Нет никакой прагмы, чтобы изменить это.

Обычная практика в Хаскеле - использовать этот стиль:

get_necklace_id :: Necklace -> Maybe Int
get_necklace_id InvalidNecklace = Nothing
get_necklace_id (Necklace x) = Just x

Использование магического возвращаемого значения «-1» является распространенным стилем в языках типа C с более простыми системами типов. Но обратите внимание, что Maybe Int изоморфен Necklace, поэтому он добавляет немного в простейшем случае (кроме доступа к большому количеству общих функций для обработки Maybe, которые могут отсутствовать для Necklace). Если вы сделаете Necklace более сложным, тогда get_necklace_id имеет смысл.

Для более крупных проектов можно использовать шаблон Haskell или дополнительный инструмент, автоматически создающий get_necklace_id выше.

ОБНОВЛЕНИЕ: Использование fromJust не очень хорошая идея. Чтобы получить «разумные значения по умолчанию» и «режимы без сбоев», вы можете составить get_necklace_id :: Necklace -> Maybe Int с Data.Maybe.fromMaybe :: a -> Maybe a -> a (одной из общих функций обработки Maybe) следующим образом:

from_necklace_id :: Int -> Necklace -> Int
from_necklace_id default = fromMaybe default . get_necklace_id

a_necklace_id :: Necklace -> Int
a_necklace_id = from_necklace_id (-1)

a_necklace_id идентичен вашей функции, которая заменяет InvalidNecklace на (-1). Код, который требует другого значения по умолчанию, может использовать from_necklace_id.

4 голосов
/ 25 января 2012

Почему вы не можете иметь тип Ожерелье, представляющий только действительные Ожерелья, и Может быть, Ожерелье для случаев, когда Ожерелье может быть недействительным?Или, избегая использования Maybe, что-то вроде (обратите внимание, что я до сих пор не уверен, каким будет хорошее соглашение об именах):

data Necklace = InvalidNecklace | NecklaceData NecklaceData
data NecklaceData = NecklaceDataRec { necklace_id :: Int, meow :: Int, ... }
...