перегрузка активных шаблонов F #

Я новичок в F # и активных паттернах, и я столкнулся с аномалией, которую не могу объяснить.

module Eval =
let (|Bet|Pass|) (test:BetChoice) =
    match test with
        | BetChoice.Bet -> Bet
        | BetChoice.Pass -> Pass

let (|NoBet|Bet|Pass|) (test:Nullable<BetChoice>) : Choice<unit, unit, unit> =
    match test.HasValue with
        | true -> match test.Value with 
                      | BetChoice.Bet -> Bet
                      | BetChoice.Pass -> Pass
        | false -> NoBet

let FlipByWinner ((value:int), (awins:bool)) =
    match awins with
    | true -> (value, -value)
    | false -> (-value, value)

let Evaluation (awins:bool) (player11:BetChoice) (player21:BetChoice) (player12:Nullable<BetChoice>) =
     match player11 with
     | Pass -> match player21 with
               | Pass -> FlipByWinner(1, awins)
               | Bet-> match player12 with
                       | Bet -> FlipByWinner(2, awins)
                       | Pass -> FlipByWinner(1, false)
                       | NoBet -> raise (System.ArgumentException("invalid strategy"))
     | Bet ->  match player21 with
               | Bet -> FlipByWinner (2, awins)
               | Pass -> FlipByWinner (1, false)

Это не компилируется. С небольшими изменениями я могу заставить его работать так, как задумано, но тот факт, что я точно не знаю, что происходит, заставляет меня немного нервничать ... второй шаблон можно переименовать в "(| NoBet | Bet1 | Pass1 | ) "и связанные с ним шаблоны меняются по всему коду, тогда он работает, но я действительно не понимаю, почему здесь возникает исключение несоответствия типов.

Также есть ли хороший способ справиться с двумя активными паттернами, которые почти идентичны, но не совсем? похоже, должен быть способ объединить общие вещи воедино. (в качестве примечания, похоже, что отступы были испорчены при копировании / вставке, это все правильно является частью модуля Eval).


person Snark    schedule 08.09.2010    source источник
comment
Поскольку вы сказали, что вы новичок в F #, я подумал, что предлагаю тип Option вместо типа Nullable, если вам не нужно взаимодействовать с другими языками .NET, поскольку Option лучше работает с сопоставлением.   -  person Gideon Engelberth    schedule 10.09.2010


Ответы (1)


Да, первая проблема имеет смысл. У вас не может быть двух активных тегов шаблонов с одинаковыми именами в одном пространстве имен. Это связано с тем, как компилятор F # фактически генерирует имена активных шаблонов. Если вы посмотрите в отражатель, то код, сгенерированный для:

type Alpha = Foo | Bar
let (|Foo|Bar|) = ...

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

В любом случае, вопрос, который вам нужен, заключается в том, что делать, если у вас есть два набора активных паттернов, которые в основном похожи / albiet различны. Я бы посоветовал вам использовать частичные активные шаблоны. См. Это сообщение в блоге .

В этом примере похоже, что вам нужно что-то вроде:

let (|IsBet|_|) = ...
let (|IsPass|_|) = ...

Таким образом, вы можете найти совпадение одновременно с игроком 11 и игроком 21, например:

match player11, player21 with
| IsPass & IsPass -> ...
| IsPass & IsBet  -> ...
| IsBet  & IsPass -> ...
| IsBet  & IsBet  -> ...

Это должно иметь большое значение для очистки кода.

person Chris Smith    schedule 08.09.2010
comment
поэтому шаблоны возвращают логические значения, позволяющие не беспокоиться о типе, допускающем значение NULL, а также делают код более сжатым ... не уверен, имеет ли это смысл, но спасибо, это помогло. - person Snark; 09.09.2010
comment
Фактически, частичные активные шаблоны могут возвращать любую форму данных. Например, шаблон IsBet может возвращать сумму ставки. матч player11 с | Сумма IsBet с суммой ›100 -› printfn Ставка больше 100 $! - person Chris Smith; 09.09.2010