Cogito Ergo Sum
Cogito Ergo Sum

Reputation: 810

How to read in csv with FSharp.Data

I am trying to use the CsvProvider from FSharp.Data following the example here: http://fsharp.github.io/FSharp.Data/library/CsvProvider.html

In the example they define a type and then use the type but it looks like they are doing it in interactive mode. I am trying to do the same thing in my main but I don't understand why it doesn't work.

In the eample they do the following:

type Stocks = CsvProvider<"../data/MSFT.csv"> 

This is my attempt:

open System
open System.IO
open FSharp.Data

    // Process execution params
    // args(0) = nesting level
    // args(1) = csv file name
[<EntryPoint>]
let main (args: string[]) =
    printfn "Arguments passes in: %A" args
    // F# searches the following directory at runtime : C:\Users\userName\source\repos\crawlerF\crawlerF\bin\Debug\netcoreapp2.1\test.csv'.
    // It is therefore necessary to provide the top level of the repo for the file read by prepending ../../../../ to the file name
    let filePath = "../../../../" + args.[1]  
    type urls = CsvProvider<filePath>
0

I get a few syntax errors when I try to define the urls variable. Why is this the case? I am new to F# so a more novice explanation would be appreciated.

Thanks.

Upvotes: 1

Views: 838

Answers (1)

rob.earwaker
rob.earwaker

Reputation: 1286

There are a few things going on here:

  1. You can't define types within a function, so you need to move the definition of urls out of the main function and into a module or namespace.

  2. Type providers give you an easy way to generate types rather than defining them yourself manually, but these types still need to be generated at compile-time. This means that any parameter you pass to a type provider must be static, i.e. must have a value at compile-time.

    In your case, the filePath of your sample CSV file is the only parameter you're passing to the CsvProvider, but because this value is derived from a command line argument it is only known at runtime. To pass a static file name to the CsvProvider you can use a literal string, e.g.

    // Option 1: Use an inline literal string, like in the example from FSharp.Data.
    type Urls = CsvProvider<"test.csv">
    
    // Option 2: Use a named literal so that the compiler knows this value is static.
    let [<Literal>] FilePath = "test.csv"
    type Urls = CsvProvider<FilePath>
    
  3. This may just be a formatting error in your code sample, but in order to return the 0 from your main function, it must be indented so that it's recognised as the last expression of that code block.

Putting this all together gives you something like the following:

open FSharp.Data

module Program =
    // The `Urls` type is now defined within a module.
    // This type is generated at compile-time from the sample file.
    // Assumes the sample file 'test.csv' exists in the project folder at compile-time.
    type Urls = CsvProvider<"test.csv">

    [<EntryPoint>]
    let main args =
        // Use the generated `Urls` type to load some data at runtime.
        let urls = Urls.Load("...")
        // ...
        // Return an integer exit code.
        0

Upvotes: 3

Related Questions