F # - конструирование вложенных типов - PullRequest
2 голосов
/ 24 сентября 2019

Полагаю, это довольно простой вопрос F #:

Типы:

type Id1 =
    | Id1 of int

type Id2 =
    | Id2 of string

type Id =
    | Id1
    | Id2

type Child = {
    Id : Id;
    Smth : string list
    }

type Node =
    | Child of Child
    | Compos of Node * Node

, где Node и Child должны представлять собой замену шаблона проектирования Composite OOP.

Проблема в том, что я не могу создавать экземпляры типов следующим образом:

let i1 : Child = {Id = Id1(1); Smth = []}   //Id1 value is not a function and cannot be applied
let i2 : Child = {Id = Id1(1); Smth = []}   //same
let i3 : Node = Compos(i1, i2)  //Expression expected Node but has Child

, где в комментариях приводятся ошибки компилятора.

1 Ответ

4 голосов
/ 24 сентября 2019

Ваше определение Id имеет ту же семантику, что и следующая:

type T = int | string

Такой тип, который представляет собой сочетание двух других типов, обычно называют «недискриминационным объединением»,Чтобы быть справедливым, есть языки, которые поддерживают недискриминационные объединения (например, TypeScript), но F # не является одним из них.F # является частью семейства языков ML, которые поддерживают дискриминационные союзы.«Дискриминационная» часть означает, что частям объединения необходимо дать отличительное свойство, «дискриминатор», который чаще называют «конструктором».Например:

type T = X of int | Y of string

Здесь X и Y являются конструкторами типа T.Они используются при построении значений, чтобы сказать, какой вариант строится:

let t = X 42

И они используются при сопоставлении с образцом, чтобы сказать, какой вариант ожидается:

let f t = match t with
    | X n -> printfn "it's a number: %d" n
    | Y s -> printfn "it's a string: %s" s

У вас действительно есть одиниз них в вашем собственном коде - Node, - поэтому меня немного расстраивает, почему вы допустили эту ошибку с Id, но не с Node.

Если вы хотите построить значения Id как вы делаете в определении i1 и i2 - то есть Id1(1), - тогда вам нужно сделать Id различимым объединением с одним из конструкторов с именем Id1 и приняв int запараметр.Из остального вашего кода я могу догадаться , что другой конструктор должен быть Id2 и взять string:

type Id = Id1 of int | Id2 of string

Теперь интересно отметитьявляется то, что, хотя мой пример типа T выше не будет компилироваться, ваш тип Id компилируется просто отлично.Почему это так?

Причина в том, что int и string имеют неправильную прописную букву (идентификаторы объединенного регистра должны быть в верхнем регистре), но Id1 и Id2 подойдут в этом отношении.Когда вы определяете свой тип как type Id = Id1 | Id2, это совершенно законно, но эти Id1 и Id2 не имеют никакого отношения к ранее определенным типам Id1 и Id2.Просто они имеют одно и то же имя, и в F # допускается использовать то же имя, что и в предыдущих определениях.Это называется «затенение».

Ваш тип Id в итоге получил два конструктора, Id1 и Id2, оба из которых не имеют параметров.Вот почему компилятор жалуется, когда вы пытаетесь передать 1 в качестве параметра Id1, когда вы пишете Id1(1): « Id1 не является функцией и не может быть применен »

...