F # / OCaml: Как избежать совпадения шаблонов? - PullRequest
4 голосов
/ 28 июля 2011

Посмотрите на этот код F # / OCaml:

type AllPossible =
    | A of int
    | B of int*int
    | ...
    | Z of ...

let foo x =
    ....
    match x with
    | A(value) | B(value,_) ->                   (* LINE 1 *)
        (* do something with the first (or only, in the case of A) value *)
        ...
        (* now do something that is different in the case of B *)
        let possibleData = 
            match x with
            | A(a) -> bar1(a)
            | B(a,b) -> bar2(a+b)
            | _ -> raise Exception    (* the problem - read below *)
        (* work with possibleData *)
    ...
    | Z -> ...

Так в чем же проблема?В функции foo мы сопоставляем шаблон с большим списком типов.Некоторые из типов имеют общую функциональность - например, они выполняют общую работу, поэтому мы используем «| A | B ->» в строке 1 выше.Мы читаем единственное целое число (в случае A) или первое целое число (в случае B) и делаем что-то с ним.

Далее мы хотим сделать что-то совершенно другое, в зависимости отработаем ли мы на A или B (т.е. звоним bar1 или bar2).Теперь нам нужно снова сопоставить с образцом, и вот проблема: в этом вложенном сопоставлении с образцом, если мы не добавим правило 'catchAll' (то есть '_'), компилятор жалуется, что мы пропускаем случаи - т.е.учтите, что здесь могут произойти только A и B.

Но если мы добавим правило catchAll, то у нас будет гораздо более серьезная проблема: если в какой-то момент мы добавим больше типов в список LINE1 (т.е. в строку'| A | B ->' ... тогда компилятор НЕ поможет нам во вложенном сопоставлении - '_' поймает их, и в RUNTIME будет обнаружена ошибка. Одна из самых важных возможностей сопоставления с образцом -то есть обнаружение таких ошибок во время компиляции - потеряно.

Есть ли лучший способ написания такого рода кода, без необходимости повторять какую-либо работу, разделяемую между A и B, в двух отдельных правилах для A и B? (или помещая общую работу A-and-B в функцию, созданную исключительно для «локального совместного использования кода» между A и B?)

EDIT : обратите внимание, что можноутверждают, что F # coВ этом случае поведение mpiler является ошибочным - он должен быть в состоянии обнаружить, что нет необходимости в сопоставлении за пределами A и B во вложенном сопоставлении.

Ответы [ 5 ]

5 голосов
/ 28 июля 2011

Если тип данных установлен в камне - я бы также предпочел локальную функцию.

В противном случае в OCaml вы также можете использовать открытые (иначе полиморфные) варианты:

type t = [`A | `B | `C]
let f = function
| (`A | `B as x) ->
  let s = match x with `A -> "a" | `B -> "b" in
  print_endline s
| `C -> print_endline "ugh"
4 голосов
/ 28 июля 2011

Я бы просто поместил общую логику в локальную функцию, которая должна быть и быстрее, и более удобочитаемой.Совпадения, вложенные таким способом, довольно сложны, и использование общей логики в локальной функции позволяет отказаться от дополнительного соответствия в пользу чего-то, что все равно будет встроено.

1 голос
/ 31 июля 2011

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

Компилятор C # не жалуется на следующее, и вы этого не ожидаете:

var b = true;

if (b)
    if (!b)
       Console.WriteLine("Can never be reached");
1 голос
/ 29 июля 2011

Возможно, лучшее решение - это вместо

type All =
    |A of int
    |B of int*int

у вас есть

type All = 
    |AorB of int * (int Option)

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

1 голос
/ 28 июля 2011

Хммм, похоже, вам нужно проектировать тип данных немного по-другому, например:

type AorB = 
    | A of int
    | B of int * int

type AllPossible =
    | AB of AorB
    | C of int
    .... other values

let foo x =
    match x with
    | AB(v) -> 
        match v with
        | A(i) -> () //Do whatever need for A
        | B(i,v) -> () // Do whatever need for B
    | _ -> ()
...