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?
It isn't correct because the string literal is not a pointer. It is an array. The literal does implicitly convert to a pointer to the first element of the array (just like all arrays do), but that result of the conversion is an rvalue. And an lvalue reference to non-const cannot be bound to an rvalue.
In Python, when you prefix a string with the letter r or R such as r'...' and R'...' , that string becomes a raw string. Unlike a regular string, a raw string treats the backslashes ( \ ) as literal characters.
A "string literal" is a sequence of characters from the source character set enclosed in double quotation marks (" "). String literals are used to represent a sequence of characters which, taken together, form a null-terminated string. You must always prefix wide-string literals with the letter L.
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With