Search code examples
f#unit-type

Confusing F# compiler message


The following snippet illustrates the error I'm getting. Even though both match branches return the same thing; I get error, "This expression was expected to have type unit but here has type 'a -> unit" I have no clue what the compiler wants here...

open System.IO 

let FileContent contents =  
  match contents with  
  | "" -> None  
  | c -> Some(c)  

let WriteSomething (contents:string) =  
  let writer = new StreamWriter("")  
  writer.Write( contents ) |> ignore  

let DoStuffWithFileContents =  
  let reader = new StreamReader( "" )  
  let stuff = reader.ReadToEnd()  
  match stuff |> FileContent with  
  | Some(c) -> WriteSomething c  
               |> ignore  
  | None -> ignore  // <- error on "ignore"

Solution

  • The ignore operator is actually a function that takes a single input and returns the unit type (F#'s equivalent of void). So when you have -> ignore you're returning the ignore function.

    Instead, use () to represent the value of the unit type:

      | Some(c) -> WriteSomething c  
                   |> ignore  
      | None -> ()
    

    But actually, since StreamWriter.Write returns void, all these ignore's are unnecessary. You could just as easily write this as:

    let WriteSomething (contents:string) =  
      let writer = new StreamWriter("")  
      writer.Write(contents)   
    
    let DoStuffWithFileContents =  
      let reader = new StreamReader("")  
      let stuff = reader.ReadToEnd()  
      match stuff |> FileContent with  
      | Some(c) -> WriteSomething c  
      | None -> () 
    

    Or even better, use Option.iter:

    let WriteSomething (contents:string) =  
      let writer = new StreamWriter("")  
      writer.Write(contents)
    
    let DoStuffWithFileContents =  
      let reader = new StreamReader("")  
      let stuff = reader.ReadToEnd()  
      stuff |> FileContent |> Option.iter(WriteSomething)