2012-02-15 2 views
8

Я знаю различия между data, newtype и type очень хорошо. Я пишу небольшой скрипт, который будет строить какое-то дерево синтаксиса. Практически все типы имеют один конструктор. Я избегаю type для обеспечения безопасности (несколько разных типов могут иметь один и тот же тип в Haskell). В этом случае меня не волнует лень/строгость, и я не забочусь о производительности (эта часть отнюдь не критическая по производительности). Я в основном сосредоточен на стиле. У меня есть три варианта:Еще один новый тип данных (стилистический вопрос)

  1. Использовать только data. Это нормально, за исключением того, что у меня много типов с одним конструктором с одним аргументом. Код выглядит как расточительно ... Хотя мне все равно, что производительность, но это просто не так.
  2. Использовать только newtype. Это приводит к множеству уродства с кортежами в случае нескольких параметров.
  3. Mix data и newtype, которые несколько выглядят неравномерно и немного раздражают. Я бы предпочел, чтобы все типы были объявлены одним последовательным способом.

Я нахожусь в дилемму выбора между 1 и 3.

ответ

11

В этом случае, я хотел бы использовать data повсеместно, в течение нескольких причин. Во-первых, для согласованности с случаями с несколькими аргументами (которые должны определенно быть data, а не newtype).

Во-вторых, и самое главное, newtype имеет различную семантику к data! Конструктор newtype является строгим, в отличие от data, которые являются нестрогими, если вы явно не используете строгие поля. Даже если вы не заботитесь о строгости, или все поля вашего data s строги, есть еще subtle differences.

Я не думаю, что один-конструктор, один аргумент data типы являются расточительными - синтаксически, они такие же легкие, как и newtype, и семантически, мне кажется более важными.

Вы сказали, что вы не обеспокоены производительности, но если во время выполнения бокса над головой из dataбыл действительно неудобно, то вы можете смешать их, до тех пор, как вы знаете семантические различия. Однако, если вы используете -funbox-strict-fields, тогда GHC может оптимизировать простейший однострочный, один аргумент data s для вас, если они встречаются как строгие поля в других типах данных.

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

6

Когда я строй реальных программ, которые не делают тонкие вещи с ленью, я почти всегда использую newtype для типов данных с одним конструктором и аргументом и data для всего остального:

data Foo = FooA | FooB Int 
data Bar = BarA Int Foo 
newtype Baz = Baz Bar 

На по крайней мере, если вы оказываетесь писать

newtype Foo = Foo (X,Y) 

семантика идентичны для

data Foo = Foo X Y 

поэтому вы можете использовать версию data, потому что она красивее. Действительно

data Foo = Foo Int 
newtype Bar = Bar Int 

действительно различаются по семантике, но ни в коем случае, что заканчивает тем, что важно для «реальных» программ, в которых мы не ожидаем, чтобы знать разницу между _|_ и Foo _|_ (потому что все значения все равно).

Есть еще одна вещь, на которую нужно смотреть: единообразие в декларациях - это что-то должно быть wary of. Это указывает на то, что существует уровень абстракции, который вы не кодируете в своей программе, что вы оставляете неявным. Посмотрите, можете ли вы кодировать этот уровень до тех пор, пока не будет существовать структура параллельного объявления, которую можно использовать. Это не всегда возможно, но старайтесь приблизиться.

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