Soldalma
Soldalma

Reputation: 4758

Why can't I turn this string into a Literal?

I need to convert a string into a Literal so I can pass it as an argument to CsvProvider. But I am not able to do it. The code below runs with no problems:

open System.IO
open FSharp.Data
open FSharp.Data.JsonExtensions

let charSwitch (a: char) b x =
    if x = a then
        b
    else
        x

let jsonDataPath = Path.Combine(__SOURCE_DIRECTORY__, @"data\fractal.json")
let jsonData = JsonValue.Load(jsonDataPath)

/// Path with traded assets
let trp = ((jsonData?paths?tradedAssets).AsString() |> Core.String.map (charSwitch '\\' '/')).ToString()
printfn "trp is a standard string: %s" trp
// trp is a standard string: H:/Dropbox/Excel/Data/Fractal/Traded.csv

However, when add the following two lines

[<Literal>]
let tradedPath = trp

at the end I get the message This is not a valid constant expression or custom attribute value.

I even tried to make a copy of trp but that did not help.

Any way to circumvent this problem?

Upvotes: 5

Views: 648

Answers (2)

s952163
s952163

Reputation: 6324

Looking at your last comment you're trying to use the CsvProvider, you can of course use something else to parse the csv file, but it's also possible to use [<Litera>] on the __SOURCE_DIRECTORY__ as well as give a ResolutionFolder argument (this has to be a Literal though) to the provider. Here are two examples, one uses a sample in the Project root to create the type but then uses a command line argument for the actual file. The other one uses relative path to parse the file.

open System
open FSharp.Data
open FSharp.Data.JsonExtensions


#if INTERACTIVE
#r @"..\packages\FSharp.Data.2.3.2\lib\net40\FSharp.Data.dll"
#endif 


[<Literal>]
let file = __SOURCE_DIRECTORY__ + @"\file1.csv"
[<Literal>]
let path3 = __SOURCE_DIRECTORY__
[<Literal>]
let path4 = "."

type SampleFile = CsvProvider<file,HasHeaders=true>
type SampleFile3 = CsvProvider<"file1.csv",HasHeaders=true,ResolutionFolder=path3>


[<EntryPoint>]
let main argv = 

    //let nonLiteralPath = @".\file1.csv" // you could hardcode this in the file but:
    let nonLiteralPath = argv.[0]  // you can also use a path specified on the command line
    let DataFile = SampleFile.Load(nonLiteralPath)
    [for row in DataFile.Rows -> row.``Key #1``]  |> printfn "%A"
    let x= SampleFile3.GetSample()  // use a relative path, this will be the root of the project at design time
                                    // or the root of the exe at the execution time
    [for row in x.Rows -> row.``Key #2``] |> printfn "%A"   

    printfn "%A" argv

And for the output:

enter image description here

Upvotes: 3

TheInnerLight
TheInnerLight

Reputation: 12184

Sadly, you can't magically turn an ordinary value into a literal value by applying the [<Literal>] attribute to it.

The special thing about literal values is that are compiled as a constant and that means they have to be determinable at compile time.

For example, this is a literal string:

[<Literal>]
let testLiteral = "This is a literal string"

You can combine several literal strings into a new literal string:

[<Literal>]
let a = "a"
[<Literal>]
let b = "b"
[<Literal>]
let ab = a + b

You cannot apply arbitrary functions to literals because then they would not be determinable at compile time.

More about literals.

Upvotes: 6

Related Questions