If an expression returns a value, you'll probably want to do something with it. F# helps you make sure you do so by giving a warning if you don't:
let sendMessage message =
let messageId = Guid.NewGuid()
printfn $"Sending message '%s{message}'; message ID: {messageId}"
messageId
sendMessage "Hello World"
//The result of this expression has type 'System.Guid' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'
But sometimes you really don't want to do anything with the returned value, for example if you run a function just for it's side effect. As mentioned by the warning, the ignore
function, with signature T -> unit
, allows you to explicitly ignore values, getting rid of the warning:
sendMessage "Hello World" |> ignore
This is great, because it makes it clear that we did not just forget about the value, but explicitly don't want to use it. There's a subtle risk in using this though; take the following situation, where we added a second argument to the sendMessage
function:
let sendMessage message recipient =
let messageId = Guid.NewGuid()
printfn $"Sending message '%s{message}' to %s{recipient}; message ID: {messageId}"
messageId
sendMessage "Hello World!" |> ignore
If you run this code, nothing will be printed; instead of calling the sendMessage
function, we partially apply it. If you remove the ignore
function, a new warning is revealed:
This expression is a function value, i.e. is missing arguments. Its type is string -> System.Guid.
Fortunately, there's a simple way to avoid this; like any generic function, ignore
can take a type parameter; this means we'll get a compiler error when we pass the wrong type to it:
sendMessage "Hello World" |> ignore<Guid>
// Type mismatch. Expecting a
// '(string -> Guid) -> 'a'
// but given a
// 'Guid -> unit'
// The type 'Guid' does not match the type 'string -> Guid'
sendMessage "Hello World" "John Doe" |> ignore<Guid>
I'd recommend adding a type parameter any time you use the ignore
function; it can save you a lot of headache when functions all of a sudden stop being executed after refactoring. By explicitly specifying the expected return type, you ensure that the ignore
function receives the correct value and that the side-effecting function is indeed fully applied before its result is discarded. This practice adds a layer of safety, especially in larger codebases where subtle changes can lead to unexpected behavior.
Another way to get rid of this warning is binding it to
_
. This method is also sensitive to ignoring a partially applied function!