Я новичок в Haskell и в настоящее время перехожу через Real World Haskell. В книге говорится, что конструктор типов используется только в сигнатуре типа, в то время как конструктор значений используется в фактическом коде. Он также дает пример объявления, показывающего, что имена для обоих не зависят друг от друга. Почему в первую очередь нужны два конструктора, если только один из них используется в реальном коде? Поскольку мы не будем использовать конструктор типа в фактическом коде, какая цель выполняет конструктор типа?
Почему в конструкторе типов в Haskell существует конструктор значений?
Ответ 1
Возможно, имена немного вводят в заблуждение. Конструктор типа представляет имя типа, который вы объявляете. Они называются так, потому что они строят типы, а не значения: действительно, будучи (возможно) параметризованными по переменным типа, они определяют семейство типов. Они действуют как С++-шаблоны и Java-дженерики. В data MyType a b = Constr a b
MyType является конструктором типа, который принимает два типа a
и b
для создания нового типа (MyType a b)
.
Конструктор значений - это единственная часть, которую вы бы назвали "конструктором" на других (объектно-ориентированных) языках, потому что вам нужно, чтобы строить значения для этого типа. Итак, в предыдущем примере, если вы берете конструктор значений Constr :: a -> b -> MyType a b
, вы можете создать значение Constr "abc" 'd' :: MyType [Char] Char
.
Ответ 2
Это немного похоже на высказывание "зачем нам нужны классы и объекты, если объекты - это единственное, что действительно работает?"
Два типа конструкторов выполняют разные задания. Конструкторы типов входят в сигнатуры типов. Конструкторы значений входят в исполняемый код.
В простейшем случае тип "конструктор" - это просто имя типа. В простейшем случае тип имеет только один конструктор значений. Таким образом, вы получаете такие вещи, как
data Point = Point Int Int
Вы можете сказать себе: "Почему, черт возьми, мне нужно дважды писать Point
?"
Но теперь рассмотрим менее тривиальный пример:
data Tree x = Leaf x | Branch (Tree x) (Tree x)
Здесь Tree
- конструктор типа. Вы даете ему аргумент типа, и он "конструирует" тип. Итак, Tree Int
- это один тип, Tree String
- другой тип и т.д. (Подобно шаблонам на С++ или дженерикам в Java или Eiffel.)
С другой стороны, Leaf
является конструктором значений. Учитывая значение, он выдает дерево из дерева" > w630 > . Таким образом, Leaf 5
является значением Tree Int
, Leaf "banana"
является значением Tree String
и т.д.
Аналогично для Branch
. Он принимает два значения дерева и создает дерево node с этими деревьями как дочерние. Например, Branch (Leaf 2) (Leaf 7)
- это значение Tree Int
.
Ответ 3
Один удобный способ получить интуицию о типах и значениях состоит в том, что первые значения время компиляции, а последние - время выполнения. Другими словами, конструкторы Type являются конструкторами значений в наборе типов Haskell, с единственной целью набирать вашу программу во время компиляции. Это также означает, что вы не можете создать тип во время выполнения, и вы не можете построить значение во время компиляции.
Итак, поскольку вы не можете явным образом входить во время выполнения на основе значения типа (хотя вы можете неявно с помощью typeclasses), конструкторы Type полностью бесполезны в качестве объектов времени выполнения и во многих случаях полностью отсутствуют в окончательный двоичный код. И наоборот, поскольку конструкторы значений позволяют создавать значения в наборе своего типа во время выполнения, они абсолютно бесполезны как объект времени компиляции.
Из-за этого простого свойства конструкторы Type и конструкторы Value могут однозначно делиться именами.