FSharp.Data optional type throws exception - f#

I have some Xml with one of the nodes as follows:
<code>23</code>
FSharp.Data correctly infers the type of "Code" to be an option int. My problem is when the node is empty as follows:
<code />
I get the following exception
Value is missing at <StartupCode$FSharp-Data>.$TextRuntime.GetNonOptionalValue#109-4.Invoke(String message) in C:\Git\FSharp.Data\src\CommonRuntime\TextRuntime.fs:line 109
Is there any way I can get back Option.None instead of it throwing an exception?
EDIT: For types inferred as option string this works as expected.

Did you try the SampleIsList one ? Here is a sample working fine.
#r "packages/FSharp.Data/lib/net45/FSharp.Data.dll"
#r "System.Xml.Linq"
open FSharp.Data
type Code = XmlProvider< """<samples><code>123</code><code /></samples>""", SampleIsList=true >
Code.Parse("<code>123</code>") = Some 123 //true
Code.Parse("<code />") = None //true

Related

F#, the value or constructor 'List' is not defined

I am copying this code out of a book on F#. The compiler is supposed to create a generic function.
Instead, I am getting an error
"FS0039: The value or constructor 'List' is not defined."
Why?
let createList(first, second) =
let output = List()
output.Add(first)
output.Add(second)
output
// expected output below
// val createList : first:'a * second:'a -> List<'a>
I added this line to the script
open System.Collections.Generic
unlike C#, Visual Studio F# isn't good at telling you when you need to add an open statement like this.

How can I use a command line argument as the argument for a type provider?

What's the proper way to use a dynamic value as the argument for a type provider like CsvProvider? I'd expect this to work:
open System.IO
open FSharp.Data
[<EntryPoint>]
let main argv =
type Stock = CsvProvider<argv.[0]>
let stockData = Stock.Load(argv.[0])
for row in stockData.Rows do
printfn "(%A, %A, %A, %A)" row.High row.Low row.Open row.Close
0 //Exit
What am I doing wrong?
You can't use a command-line argument as the static argument for the type provider. The line type Stock = CsvProvider<argv.[0]> requires the parameter to CsvProvider to be a compile-time constant, because the types generated by the type provider are created a compile-time, not at run-time.
You can supply a different value to the Load function, and this can be a run-time value, as in your line Stock.Load(argv.[0]), but you will need to use a compile-time constant file name or sample data that matches the expected layout of the file being passed as a command-line argument, so that the types generated at compile-time will match the structure of the file being passed in at run-time (even though the data may be different).
For any type provider you're going to need a schema so that the compiler can understand what the shape of your data looks like. Consequently it has to be something that's available at compile time. One way to do this is to put it in a file:
High,Low,Open,Close
29.53,29.17,29.45,29.23
29.70,29.40,29.61,29.50
29.65,29.07,29.07,29.56
29.57,29.18,29.47,29.34
Which you can then import outside of your main function like so:
// or whatever you called the file
type Stock = CsvProvider<"schema.csv">
CsvProvider also lets you just give it a schema inline if you prefer:
type Stock = CsvProvider<"High,Low,Open,Close
29.53,29.17,29.45,29.23">
Here it is in the context of the whole program:
open System.IO
open FSharp.Data
type Stock = CsvProvider<"schema.csv">
[<EntryPoint>]
let main argv =
let stockData = Stock.Load(argv.[0])
for row in stockData.Rows do
printfn "(%A, %A, %A, %A)" row.High row.Low row.Open row.Close
0

ContainsKey returns unit and not bool?

It's odd to me that the following code throws a compile time error. I'm not sure why ContainsKey is returning unit. The documentation says it returns bool.
open System.Collections.Generic
let mydict = new Dictionary<string,'a>()
if(mydict.ContainsKey "mykey") then
mydict.["mykey"] = newkey
error FS0001: This expression was expected to have type
'bool' but here has type
'unit'
Am I missing something here?
if is an expression so both branches must have the same type. If the else branch is not specified an empty one of type unit is inserted. This means your then branch must also have type unit. However mydict.["mykey"] = newkey has type bool. If you want to insert a new value for mykey you should use <- instead of =:
if(mydict.ContainsKey "mykey") then
mydict.["mykey"] <- newkey

Calling ES 3rd party script method from F# Fable

I'm trying to get Fable to compile the following code correctly but am unable to do so:
module AppView
#r "../../../node_modules/fable-core/Fable.Core.dll"
open Fable.Core
open Fable.Import.Browser
open Fable.Core.JsInterop
[<Import("default", from="../../../js/3rd/riot.js")>]
module riot_js =
let mount:((string*obj)->array<obj>) = jsNative
type App
(
tagName:string
,state
,store
) =
member public x.AppTag =
(riot_js?mount ("app", state))
// does not compile: The value or constructor 'riot_js' is not defined.
// (riot_js.mount ("app", state))
// compiles wrongly to: riot_js.mount(["app", this.state]);
Trying riot_js?mount would magically cause riot_js to not exist any more and trying riot_js.mount compiles into riot_js.mount(["app", this.state]);.
Mount does not take one argument but 2 but it either won't transpile or transpile wrong.
For now I have one of the strangest looking solutions:
[<Emit("riot_js")>]
let riot_js (x: int): obj = jsNative
...
((riot_js 1)?mount ("app", state))
This returns an array but again Fable does not let me take the first element in a "normal" way:
((riot_js 1)?mount ("app", state))?[0]
Gives me red on [ with error Unexpected symbol '[' in expression. Expected identifier, '(' or other token.
And
((riot_js 1)?mount ("app", state)).[0]
Gives red on everything with error The field, constructor or member 'Item' is not defined.
The following "works"
((riot_js 1)?mount ("app", state))?``0``
And is compiled to:
riot_js.mount("app", this.state)["0"];
Not the best result someone can get. I'll let this question sit for a while and set a bounty on it for a week or so before opening 2 issues with Fable.
The following seems to compile to the right ES and does not need the ? so it'll be strongly typed.
open Fable.Core.JsInterop
type Riotjs =
{
mount:(System.Func<string,obj,string []>)
}
let riot = (importAll<obj> "../js/3rd/riot.js") :?> Riotjs
let app = riot.mount.Invoke("app",(createObj []))
I set initial state to type obj but can use a strong typed application state as well.
ES generated is:
export var app = riot.mount("app", {});

How Do I Use A Variable As The Formatting String With Sprintf?

I feel like a total noob for having to ask this but it's got me stumped.
I set a format string like this:
let fs = "This is my format test %s"
Then I attempt to use it like so:
let s = sprintf fs "testing"
When I do so I get this error:
//stdin(26,17): error FS0001: The type 'string' is not compatible with the type 'Printf.StringFormat<('a -> 'b)>'
So I then tried this:
let s = sprintf (Printf.StringFormat fs) "test"
to which the REPL responded:
//stdin(28,18): error FS1124: Multiple types exist called 'StringFormat', taking different numbers of generic parameters. Provide a type instantiation to disambiguate the type resolution, e.g. 'StringFormat<_>'.
So I then tried this:
let s = sprintf (Printf.StringFormat<string> fs) "test"
And I get this:
//stdin(29,18): error FS0001: The type ''a -> 'b' does not match the type 'string'
Am I missing something painfully obvious? This is using F# 3.0 on the Mac from the Xamarin Studio F# Interactive Window.
So you actually need to create a StringFormat which has a function type as follows
> sprintf (Printf.StringFormat<string->string>("Hello %s")) "World";;
val it : string = "Hello World"
In Section 6.3.16 of the spec, an example of this is shown.

Resources