1

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.FSharpResult2[System.UInt64,System.Object]
at FsUnit.Xunit.Assert.That.Static[a](a actual, IMatcher
1 matcher)
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?

asked Feb 8, 2020 at 8:17

2 Answers 2

3

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?

answered Feb 8, 2020 at 9:32
Sign up to request clarification or add additional context in comments.

Comments

3

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.

answered Feb 8, 2020 at 20:13

1 Comment

Ah thanks for the explanation. 1 |> should equal "1" is really weird for unit tests not to fail

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.