2010-07-07 5 views
6

я хочу написать что-то вроде этого:Общие случаи в F # предвзято профсоюзы

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 в Dot (а не 2 поплавков), потому что это делает генерацию выражения проще, но они не могут быть любой Exp, просто числовой ,

EDIT: то, что я действительно хотел бы знать, является ли эти два случая в двух ПН можно рассматривать как тот же объект (вроде как Exp «включая» NumExp). Я понимаю, что Exp.Num и NumExp.Num являются полностью отдельными объектами. Томас обеспечивает хороший способ различать два случая ниже.

ответ

13

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

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 

Это всегда использует полностью квалифицированные имена, что, вероятно, является хорошей идеей, если имена достаточно просты и существуют противоречивые случаи (что может привести к путанице).

EDIT: Что касается разделения случаев - нет автоматического способа сделать это, но вы могли бы иметь дело в вашем 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) -> // ... 
+0

Спасибо Томасу, знаменательные работы творят чудеса. То, о чем я спрашивал, скорее всего было «можно ли рассматривать два случая в двух DU как одну и ту же сущность?», Или, другими словами, «может Exp» включать «NumExp?». Но из вашего ответа ответ отрицательный. Благодаря! – Mau

+2

@Mau: Я добавил некоторую информацию о совместном использовании случаев между различными дискриминационными союзами. Это невозможно, но вы можете включить его в другое. –

+0

спасибо, это именно то, что я сделал :-) – Mau

-1

Просто наблюдение: зачем вам профсоюзы, построенные таким образом?

я выбрал бы один из двух вариантов:

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.

+0

Спасибо Muhammad. Первое решение исключается в вопросе выше (сложнее иметь дело с остальной частью кода). Второе решение является синтаксически правильным и семантически, что я хотел бы, но создает другую структуру данных из того, что мне нужно ('Exp.NumExp' - это просто пустой случай, а не поле типа' NumExp'). – Mau

2

Если это возможно (например, с использованием полиморфных вариантов в OCaml), вы можете много сделать с ним, но (к сожалению) F # не имеет этой языковой функции, поэтому в настоящее время она неспособна выразить то, что вы хотите, используя типы union. Однако вы можете использовать OOP вместо этого ...

2

Вы можете использовать interfaces as a substitute. Это добавляет немного синтаксических накладных расходов, но это лучший способ, который я нашел для этого.

type IExp = interface end 

type NumExp = 
     | Num of float 
     interface IExp 
type Exp = 
     | Dot of NumExp * NumExp 
     | Op of string * IExp * IExp 
     interface IExp 

// This function accepts both NumExp and Exp 
let f (x:IExp) = match x with 
    | :? NumExp as e -> match e with 
     | Num v -> "Num" 
    | :? Exp as e -> match e with 
     | Dot (e1,e2) -> "Dot" 
     | Op (op,e1,e2) -> "Op" 
    | _ -> invalidArg "x" "Unsupported expression type" 

// This function accepts only NumExp 
let g = function 
    | Num v -> "Num" 
+1

Хмм, я не уверен, что я следую. Как это решить проблему *, используя литерал 'Num' в обоих объединениях *? – Mau

+1

Вы можете использовать 'IExp', если вы хотите использовать как DU (объединенные 3 случая), так и' NumExp', если вы хотите использовать только 'Num'. Функция 'f' в примере функции, которая использует/принимает все 3 случая. По сути, вы сделали 'IExp' DU с 3 случаями, а' Exp' находится только в фоновом режиме. – dtech

+1

Я вижу! Отлично! – Mau

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