Search code examples
f#sastype-providers

Use a variable as the path to a file referenced by an F# type provider


How do I pass a variable path to a type provider?

I have a file that I want to load using the SAS type provider that is in different locations on different PCs.

For example, I would like to do something like:

[<Literal>]
let saspath =
    match System.Environment.MachineName with
    | "a" -> "c:/sas.sas7bdat"
    | "b" -> "d:/sas.sas7bdat"

let sasfile = new SasFileTypeProvider<saspath>()

But this is not valid. It's related to this Type provider and static argument in F# and For an F# Type Provider, how do I make a relative path work as a static parameter?, but I do not have the option of using relative paths.


Solution

  • Unfortunately, this is not easily possible. Parameters passed to type providers have to be (syntactically) constants. There are various workarounds.

    Use relative path. Can you change the code to use a relative path that will always work? This would be ideal - assuming the type provider supports this.

    Improve the type provider. You could send a PR to the SAS type provider so that it can take the file name from an local config file or and environment variable. Many of the SQL type providers do this, because it is a good solution if you need some configuration.

    Generate file with a constant. This is a bit of a hack, but you can generate a separate fsx file with the constant and #load it. You'll get red squigglies in the rest of your code (until you run the first part of the script), but if you're happy with that:

    open System.IO
    let text = 
      match System.Environment.MachineName with
      | "a" -> "module Constants\n[<Literal>] let path = \"c:/sas.sas7bdat\""
      | "b" -> "module Constants\n[<Literal>] let path = \"c:/sas.sas7bdat\""
    File.WriteAllText("C:/temp/constants.fsx", text)
    ;;
    
    #load "C:/temp/constants.fsx"
    let sasfile = new SasFileTypeProvider<Constants.path>()