Reputation: 2790
I'm doing some F# exercises and the task is to implement a function called clean
which takes a string input and returns a parsed number.
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
This is my temporary solution. But the unit test bellow failed:
[<Fact>]
let ``Cleans the number`` () =
let expected: Result<uint64,string> = Ok 2234567890UL
let parsed = clean "(223) 456-7890"
parsed |> should equal expected
It fails:
FsUnit.Xunit+MatchException : Exception of type 'FsUnit.Xunit+MatchException' was thrown. Expected: Equals Ok 2234567890UL Actual: was Microsoft.FSharp.Core.FSharpResult
2[System.UInt64,System.Object]
1 matcher)
at FsUnit.Xunit.Assert.That.Static[a](a actual, IMatcher
at PhoneNumberTest.Cleans numbers with dots() in
So I added an type annotation: let clean input: Result<uint64,string> // function body is the same
Can I avoid the type annotation and what is the technical reason that the first solution failed?
Upvotes: 1
Views: 226
Reputation: 243051
The problem here is that compiler cannot infer the type that your Result
would have in the case of a failure from just the success value Ok 2234567890UL
. When it does not know, it just uses obj
and so the default inferred type is Result<uint64, obj>
. Comparing objects of different types is always false
.
This is somewhat unfortunate, because you are comparing the two values and so, they have to have the same type. The FsUnit
library which provides should
and equal
operations is not strictly typed and allows you to compare values of different types. You can write:
1 |> should equal "1"
There is a library FsUnitTyped
which implements a typed version of FsUnit. I don't have experience with this, but it looks like you could use it and write:
[<Fact>]
let ``Cleans the number`` () =
let expected = Ok 2234567890UL
let parsed = clean "(223) 456-7890"
parsed |> shouldEqual expected
This would solve your problem - the compiler could infer that the type of expected
has to be the same as the type of parsed
, so it would automatically use the correct type.
Upvotes: 3
Reputation: 2383
Without the type annotation, your clean
function is inferred to have the generic type seq<char> -> Result<uint64,'a>
, so in the case you need the type annotation.
The reason for the compiler to infer a generic type is that you always return the OK
branch of the Result<'ok,'error>
type.
As also the name of your function suggest, maybe Result
is not the most sutiable return type here?
Upvotes: 3