F # Результат модульного теста завершается неудачно без аннотации типа. Можно ли избежать аннотации типа в этом случае? - PullRequest
1 голос
/ 08 февраля 2020

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

let clean input = 
    input
    |> Seq.filter Char.IsDigit
    |> Seq.fold (fun acc cur ->
                    if acc = "" && cur = '1' then
                        acc
                    else acc + string cur) ""
    |> uint64
    |> Ok

Это мое временное решение. Но приведенный ниже модульный тест не прошел:

[<Fact>]
let ``Cleans the number`` () =
    let expected: Result<uint64,string> = Ok 2234567890UL
    let parsed = clean "(223) 456-7890"
    parsed |> should equal expected

Не удалось:

FsUnit.Xunit + MatchException: было сгенерировано исключение типа 'FsUnit.Xunit + MatchException'. Ожидается: равно в порядке 2234567890UL Фактически: был Microsoft.FSharp.Core.FSharpResult * совпадение 1011 * 1)
в PhoneNumberTest.Cleans числа с точками () в

Поэтому я добавил аннотацию типа: let clean input: Result<uint64,string> // function body is the same

Можно ли избежать аннотации типа и по какой технической причине произошел сбой первого решения?

Ответы [ 2 ]

3 голосов
/ 08 февраля 2020

Проблема здесь в том, что компилятор не может определить тип, который ваш Result будет иметь в случае сбоя, только из значения успеха Ok 2234567890UL. Когда он не знает, он просто использует obj и поэтому выводимый тип по умолчанию - Result<uint64, obj>. Сравнение объектов разных типов всегда false.

Это несколько прискорбно, потому что вы сравниваете два значения, поэтому они должны иметь одинаковый тип. Библиотека FsUnit, которая обеспечивает операции should и equal, не является строго типизированной и позволяет сравнивать значения различных типов. Вы можете написать:

1 |> should equal "1"

Существует библиотека FsUnitTyped, которая реализует типизированную версию FsUnit. У меня нет опыта с этим, но похоже, что вы могли бы использовать его и написать:

[<Fact>]
let ``Cleans the number`` () =
    let expected = Ok 2234567890UL
    let parsed = clean "(223) 456-7890"
    parsed |> shouldEqual expected

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

3 голосов
/ 08 февраля 2020

Без аннотации типа ваша функция clean будет иметь обобщенный тип c type seq<char> -> Result<uint64,'a>, поэтому в случае, если вам нужна аннотация типа.

Причина, по которой компилятор может сделать вывод универсальный тип c заключается в том, что вы всегда возвращаете ветвь OK типа Result<'ok,'error>.

Как и предполагает ваша функция, возможно, Result не является наиболее подходящим типом возврата здесь

...