Я хочу записать что-то вроде этого:
type NumExp = Num of float
type Exp =
| Num of float
| Dot of NumExp * NumExp
| Op of string * Exp * Exp
let getValue (Num(n) : NumExp) = n
Компилятор жалуется на конфликт между NumExp
и Exp
в getValue
. Даже следующие сбои:
let getValue (nn : NumExp) = match nn with | Num(n) -> n
Существует ли способ использовать тот же случай в обоих дизъюнктных объединениях, который работает с функциями? Сами определения DU в порядке.
Я хочу использовать тот же случай, чтобы не добавлять уровень абстракции как
type Exp =
| NumExpExp of NumExp
| Dot of NumExp * NumExp
| Op of string * Exp * Exp
в Exp
определение. Я чувствую, что пропускаю что-то очень простое здесь.
Причина я имею NumExp
это, я хочу смочь 'включиться' 2 Exp
s в a Dot
(а не 2 плавания), потому что это делает генерирующиеся выражения легче, но они не могут быть никем Exp
, просто числовой.
Править: то, что я действительно хотел знать, - можно ли эти два случая в двух DUs было рассматривать как тот же объект (отчасти как Exp
"включая" NumExp
). Я понимаю теперь Exp.Num
и NumExp.Num
абсолютно отдельные объекты. Tomas обеспечивает хороший способ отличить эти два случая ниже.
Если у вас есть два размеченных объединения с конфликтующими именами наблюдений, вы можете использовать полностью определенное имя прецедента размеченного объединения:
let getValue (NumExp.Num(n)) = n
Более полный пример будет выглядеть так:
let rec eval = function
| Exp.Num(f) -> f
| Exp.Dot(NumExp.Num(f1), NumExp.Num(f2)) ->
// whatever 'dot' represents
| Exp.Op(op, e1, e2) ->
// operator
Здесь всегда используются полностью определенные имена, которые вероятно, хорошая идея, если имена достаточно простые и есть противоречивые случаи (что может привести к путанице).
РЕДАКТИРОВАТЬ: Что касается совместного использования кейсов - автоматического способа сделать это нет, но вы можете иметь кейс в вашем Exp
, который просто включает значения NumExp
. Например, так:
type NumExp =
| Num of float
type Exp =
// first occurrence of NumExp is just a name, but F# allows us to reuse
// the name of the type, so we do that (you could use other name)
| NumExp of NumExp
// other cases
При написании функции eval
вы должны затем написать (обратите внимание, что у нас больше нет проблемы с конфликтами имен, поэтому нам не нужны полностью определенные имена):
| NumExp(Num f) -> f
| Op(op, e1, e2) -> // ...
Когда это возможно (например, использование полиморфных вариантов в OCaml), вы можете многого добиться, но (к сожалению) F# не имеет такой возможности языка, поэтому в настоящее время он не способен выразить то, что вы хотите, используя типы объединения. Однако, вы можете рассмотреть возможность использования ООП вместо этого...
Просто наблюдение: зачем вам профсоюзы, построенные таким образом?
Я бы выбрал один из двух вариантов:
type NumExp = Num of float
type Exp =
| Num of float
| Dot of float * float
| Op of string * Exp * Exp
, который является более простым, или
type NumExp = Num of float
type Exp =
| NumExp
| Dot of float * float
| Op of string * Exp * Exp
В данном случае ваша функция
let getValue (Num(n) : NumExp) = n
работает так, как у вас есть одно определение NumExp
сейчас.