Совместные дела в F # дискриминационных союзах - PullRequest
6 голосов
/ 07 июля 2010

Я хочу написать что-то вроде этого:

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

РЕДАКТИРОВАТЬ : что я действительно хотел знать, так это то, можно ли рассматривать два случая в двух DU как одну и ту же сущность (вроде как Exp ", включая" ​​NumExp). Теперь я понимаю, что Exp.Num и NumExp.Num - это совершенно разные сущности. Томас предлагает хороший способ различить два случая ниже.

Ответы [ 4 ]

14 голосов
/ 07 июля 2010

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

 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) -> // ...
2 голосов
/ 16 февраля 2015

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

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"
2 голосов
/ 09 июля 2010

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

0 голосов
/ 08 июля 2010

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

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

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...