Search code examples
f#unmanagedresources

Is there an inverse to [<AutoOpen>]?


This post contains two inter-related questions that have cleaning up resources in common. I have read this SO post, along with several others on Microsoft's sites trying to determine what is a managed versus an unmanaged resource. Based on what I've read the following code block uses .Net managed resources. This is from non-DLL F# library.

namespace Toa.csv_lib
open System
open System.Threading
open System.Collections.Generic
open System.Linq
open System.Text
open System.Threading.Tasks
open System.IO
open Microsoft.VisualBasic.FileIO

[<AutoOpen>]
module csv_lib =
    let initCsvLib fn delim =
        let csvFileH = new TextFieldParser(fn:string)
        csvFileH.TextFieldType = FieldType.Delimited |> ignore
        csvFileH.SetDelimiters(delim) |> ignore
        csvFileH

    let readCsvLibLine csvFileH =
        (csvFileH:TextFieldParser).ReadFields()

    let retCsvData csvFileH =
        let csvData = new List<string[]>()

        if not (csvFileH:TextFieldParser).EndOfData then
            let column_headings = readCsvLibLine csvFileH
            csvData.Add(column_headings) |> ignore

            let read_rest_of_csv() =
                csvData.Add(readCsvLibLine csvFileH) |> ignore
                not (csvFileH:TextFieldParser).EndOfData

            while read_rest_of_csv() do ignore None
        csvData // Last expression is returned.

Even though I believe csvFileH and csvData to be managed resources, I was wondering is there an inverse to [<AutoOpen>] and does there need to be one? This would be a directive that would allow a library to would free up all the resources created in [<AutoOpen>].

I could restructure this code to have using around the csvFileH, because that handle that is needed only to read in a .csv file. But given a List<string[]> csvData type is returned, is that still considered a managed resource that will be garbage-disposed when the application shuts down.


Solution

  • All that [<AutoOpen>] does is expose the contents of the module without an open statement. That's it. It's not running any code, it's just exposing something automatically which would need to be exposed manually.

    In your case though when you're referencing this module, you're not actually executing anything. initCsvLib is a pure function that takes two args and returns an instance of csvFileH. There is no initialization code being run here. If you had for example

    module Foo =
        let expensiveThing = ExpensiveThing()
    
        let myFn arg1 arg2 =
            // This isn't run until the function myFn is called.
            let expensiveThing = ExpensiveThing()
            // ...
    

    Then you may have a problem on your hands. In that case though you'd have to recall that modules are really static classes and basically follow the same rules as static classes do in C# (thinking about when static constructors are run) and go from there.

    But if you get to that point you should instead be asking yourself why you have such an expensive operation in a constructor in the first place...