Why are some functions available only in F# script files, not in source files? - f#

I have noticed this a few times now. An example of an offending function is Array.take. In a script file I can write
[|1; 2; 4; 7; 6; 5|]
|> Array.take 3
|> Array.iter (printfn "%d")
and this works without a problem. But if I try to use Array.take in an actual source file, I get the following error
[|1; 2; 4; 7; 6; 5|]
|> Array.take 3 // RED SQUIGGLY ERROR HERE
|> Array.iter (printfn "%d")
and the error message is:
The value, constructor, namespace or type 'take' is not defined
So, what gives? Thanks in advance for your help.

I suspect that you're seeing this is due to different versions of F#. There was an attempt to regularise a lot of the List Seq and Array functions in F# 4.0, see: https://visualfsharp.codeplex.com/wikipage?title=Status
One of the functions that was added as part of that process was Array.take.
In F# interactive, no doubt you are using the latest version of F# but presumably you are not in your compiled project.
This could be because you haven't changed the version in the project settings or it could be because you have a nuget package attached your project which references a specific version of F# Core via nuget.
If you go to your project properties, you should see an a 'Target F# Runtime' setting, change this to F# 4.0. If I remember correctly, a nuget reference to a specific F# Core version will prevent you from changing that setting, in which case you'll need to delete the reference to F# core and re-add the correct version as a reference from the list of assemblies.

Related

How to do tuple augmentation

The following code is from chapter 5 of "F# 4.0 Design Patterns".
let a = 1,"car"
type System.Tuple<'T1,'T2> with
member t.AsString() =
sprintf "[[%A]:[%A]]" t.Item1 t.Item2
(a |> box :?> System.Tuple<int,string>).AsString()
The desired output is [[1]:["car"]]
However, a red squiggly appears under AsString(). "The field, constructor or member 'AsString' is not defined. Maybe you want one of the following: ToString"
This is a bit odd code sample - I suspect the point that this is making is that F# tuples are actually .NET tuples represented using System.Tuple - by showing that an extension to System.Tuple can be invoked on ordinary F# tuples.
I suspect the behaviour of F# has changed and it no longer allows this - it may have been that adding extensions was allowed on System.Tuple, but not on ordinary F# tuples, but the two have became more unified in the compiler.
However, you can do a very similar thing using the .NET-style extension methods:
let a = 1,"car"
[<System.Runtime.CompilerServices.ExtensionAttribute>]
type TupleExtensions =
[<System.Runtime.CompilerServices.ExtensionAttribute>]
static member AsString(t:System.Tuple<'T1,'T2>) =
sprintf "[[%A]:[%A]]" t.Item1 t.Item2
let st = (a |> box :?> System.Tuple<int,string>)
st.AsString()
This can actually be also invoked directly on an F# tuple value:
("car", 32).AsString()

F# CSV TypeProvider less robust in console application

I am trying to experiment with live data from the Coronavirus pandemic (unfortunately and good luck to all of us).
I have developed a small script and I am transitioning into a console application: it uses CSV type providers.
I have the following issue. Suppose we want to filter by region the Italian spread we can use this code into a .fsx file:
open FSharp.Data
let provinceData = CsvProvider< #"https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-province/dpc-covid19-ita-province.csv" , IgnoreErrors = true>.GetSample()
let filterDataByProvince province =
provinceData.Rows
|> Seq.filter (fun x -> x.Sigla_provincia = province)
Being sequences lazy, then suppose I force the complier to load in memory the data for the province of Rome, I can add:
let romeProvince = filterDataByProvince "RM" |> Seq.toArray
This works fine, run by FSI, locally.
Now, if I transition this code into a console application using a .fs file; I declare exactly the same functions and using exactly the same type provider loader; but instead of using the last line to gather the data, I put it into a main function:
[<EntryPoint>]
let main _ =
let romeProvince = filterDataByProvince "RM" |> Seq.toArray
Console.Read() |> ignore
0
This results into the following runtime exception:
System.Exception
HResult=0x80131500
Message=totale_casi is missing
Source=FSharp.Data
StackTrace:
at <StartupCode$FSharp-Data>.$TextRuntime.GetNonOptionalValue#139-4.Invoke(String message)
at CoronaSchiatta.Evoluzione.provinceData#10.Invoke(Object parent, String[] row) in C:\Users\glddm\source\repos\CoronaSchiatta\CoronaSchiatta\CoronaEvolution.fs:line 10
at FSharp.Data.Runtime.CsvHelpers.parseIntoTypedRows#174.GenerateNext(IEnumerable`1& next)
Can you explain that?
Some rows have an odd format, possibly, but the FSI session is robust to those, whilst the console version is fragile; why? How can I fix that?
I am using VS2019 Community Edition, targeting .NET Framework 4.7.2, F# runtime: 4.7.0.0;
as FSI, I am using the following: FSI Microsoft (R) F# Interactive version 10.7.0.0 for F# 4.7
PS: Please also be aware that if I use CsvFile, instead of type providers, as in:
let test = #"https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-province/dpc-covid19-ita-province.csv"
|> CsvFile.Load |> (fun x -> x.Rows ) |> Seq.filter ( fun x-> x.[6 ] = "RM")
|> Seq.iter ( fun x -> x.[9] |> Console.WriteLine )
Then it works like a charm also in the console application. Of course I would like to use type providers otherwise I have to add type definition, mapping the schema to the columns (and it will be more fragile). The last line was just a quick test.
Fragility
CSV Type Providers can be fragile if you don't have a good schema or sample.
Now getting a runtime error is almost certainly because your data doesn't match up.
How do you figure it out? One way is to run through your data first:
provinceData.Rows |> Seq.iteri (fun i x -> printfn "Row %d: %A" (i + 1) x)
This runs up to Row 2150. And sure enough, the next row:
2020-03-11 17:00:00,ITA,19,Sicilia,994,In fase di definizione/aggiornamento,,0,0,
You can see the last value (totale_casi) is missing.
One of CsvProvider's options is InferRows. This is the number of rows the provider scans in order to build up a schema - and its default value happens to be 1000.
So:
type COVID = CsvProvider<uri, InferRows = 0>
A better way to prevent this from happening in the future is to manually define a sample from a sub-set of data:
type COVID = CsvProvider<"sample-dpc-covid19-ita-province.csv">
and sample-dpc-covid19-ita-province.csv is:
data,stato,codice_regione,denominazione_regione,codice_provincia,denominazione_provincia,sigla_provincia,lat,long,totale_casi
2020-02-24 18:00:00,ITA,13,Abruzzo,069,Chieti,CH,42.35103167,14.16754574,0
2020-02-24 18:00:00,ITA,13,Abruzzo,066,L'Aquila,AQ,42.35122196,13.39843823,
2020-02-24 18:00:00,ITA,13,Abruzzo,068,Pescara,PE,42.46458398,14.21364822,0
2020-02-24 18:00:00,ITA,13,Abruzzo,067,Teramo,TE,42.6589177,13.70439971,0
With this the type of totale_casi is now Nullable<int>.
If you don't mind NaN values, you can also use:
CsvProvider<..., AssumeMissingValues = true>
Why does FSI seem more robust?
FSI isn't more robust. This is my best guess:
Your schema source is being regularly updated.
Type Providers cache the schema, so that it doesn't regenerate the schema every time you compile your code, which can be impractical. When you restart an FSI session, you end up regenerating your Type Provider, but not so with the console application. So it might sometimes has the effect of being less error-prone, having worked with a newer source.

F# Compiler Services incorrectly parses program

UPDATE:
I now realize that the question was stupid, I should have just filed the issue. In hindsight, I don't see why I even asked this question.
The issue is here: https://github.com/fsharp/FSharp.Compiler.Service/issues/544
Original question:
I'm using FSharp Compiler Services for parsing some F# code.
The particular piece of code that I'm facing right now is this:
let f x y = x+y
let g = f 1
let h = (g 2) + 3
This program yields a TAST without the (+) call on the last line. That is, the compiler service returns TAST as if the last line was just let h = g 2.
The question is: is this is a legitimate bug that I ought to report or am I missing something?
Some notes
Here is a repo containing minimal repro (I didn't want to include it in this question, because Compiler Services require quite a bit of dancing around).
Adding more statements after the let h line does not change the outcome.
When compiled to IL (as opposed to parsed with Compiler Services), it seems to work as expected (e.g. see fiddle)
If I make g a value, the program parses correctly.
If I make g a normal function (rather than partially applied one), the program parses correctly.
I have no priori experience with FSharp.Compiler.Services but nevertheless I did a small investigation using Visual Studio's debugger. I analyzed abstract syntax tree of following string:
"""
module X
let f x y = x+y
let g = f 1
let h = (g 2) + 3
"""
I've found out that there's following object inside it:
App (Val (op_Addition,NormalValUse,D:\file.fs (6,32--6,33) IsSynthetic=false),TType_forall ([T1; T2; T3],TType_fun (TType_var T1,TType_fun (...,...))),...,...,...)
As you can see, there's an addition in 6th line between characters 32 and 33.
The most likely explanation why F# Interactive doesn't display it properly is a bug in a library (maybe AST is in an inconsistent state or pretty-printing is broken). I think that you should file a bug in project's issue tracker.
UPDATE:
Aforementioned object can be obtained in a debbuger in a following way:
error.[0]
(option of Microsoft.FSharp.Compiler.SourceCodeServices.FSharpImplementationFileDeclaration.Entity)
.Item2
.[2]
(option of Microsoft.FSharp.Compiler.SourceCodeServices.FSharpImplementationFileDeclaration.MemberOrFunctionOrValue)
.Item3
.f (private member)
.Value
(option of Microsoft.FSharp.Compiler.SourceCodeServices.FSharpExprConvert.ConvExprOnDemand#903)
.expr

Any benefit for targeting F# runtime for F# 4.0 or 3.1 instead of 3.0?

In Visual Studio 2015 Preview you can select from three target F# runtimes:
Is there any benefit to targeting the newer versions? Do they give you access to additional APIs? If so, which ones? It would be great if we could generate a comprehensive list.
F# Core Library Reference
Unfortunately, I don't think there is a complete list of things that you get from referencing F# 4.0. However, looking at the list of new things on CodePlex, there are a few obvious ones:
Lots of new functions in List, Seq and Array modules (so that equivalent functionality is available in all of the modules where possible)
A number of other library additions (search the table for "Library"), including things like tryUnbox, isNull, ofObj, toObj, ofNullable, toNullable but also AwaitTask for non-generic tasks
Out of the language features, the support for quoting arguments of method calls is definitely one that requires the new F# core.
Also, I'm not quite sure which of these are actually in the preview - I suspect most of them are not.
I was able to generate a complete list of new additions to the public surface area of FSharp.Core for 3.1 and 4.0. The code I used to generate the list of differences is included and can be re-purposed.
https://gist.github.com/ctaggart/0205da3f153cd20b099d
A quick-n-dirty way to see the deltas in the public surface area is to crib the code from the FSharp.Core public surface area unit tests :-)
Create a console app with below code, and rebuild/rerun it against each version you are interested in. It will dump all public APIs in that version. You can use windiff or your diff tool of choice to compare APIs between versions.
open System.Reflection
let file = typeof<int list>.Assembly.Location
let asm = Assembly.ReflectionOnlyLoadFrom(file)
let referenced = asm.GetReferencedAssemblies()
for ref in referenced do
Assembly.ReflectionOnlyLoad(ref.FullName) |> ignore
let types = asm.GetExportedTypes()
let values =
types
|> Array.collect (fun t -> t.GetMembers())
|> Array.map (fun v -> sprintf "%s: %s" (v.ReflectedType.ToString()) (v.ToString()))
|> Array.sort
|> String.concat "\r\n"
// dump to a file or print to console
printfn "%s" values

Comparisons of items in a list

I have written this method which given a number and a list will return a new list with the number inserted into the list at the correct position based on its value. (I'm doing an Insertion sort.)
let rec insertinto number numbers =
match numbers with
| [] -> [number]
| head::tail -> if head > number then number::numbers else head::(insertinto number tail)
F# guesses the type of this method to be :
val insertinto : 'a -> 'a list -> 'a list when 'a : comparison
If I test this method with
[4; 10; 15] |> insertinto 12
I get
val it : int list = [4; 12; 10; 15]
Which is clearly wrong. The comparison 'head > number' is not working correctly.
To get it to work I have to specify the type of the numbers parameter :
let rec insertinto number numbers: int list =
Then it all works, but I dont want to use an int list all the time, I want this to work with any type of list. As long as the type implements comparison it should work, surely.
Why would this work with an int list and not a generic list? What am I missing?
edit
ok, this appears to be a Mono only problem.
I just tested the code on Mac using Mono (version 2.8) and MonoDevelop with the latest F# integration and the first function (without type annotations) works as expected.
However, I wouldn't be too surprised if this was a bug in earlier version of Mono. There were all sorts of issues in 2.6.x. Giving incorrect result is weird error (more common was some error message or crash), but it may be caused by some issue. Can you check your version of Mono?
I'm using this and it works fine:
fsmac:~ tomaspetricek$ mono -V
Mono JIT compiler version 2.8 (tarball Thu Oct 7 12:23:27 MDT 2010)
Copyright (C) 2002-2010 Novell, Inc and Contributors. www.mono-project.com
Although you can run F# on Mono 2.6.x, it is highly recommended to use 2.8 (which fixes many issues with generics that were blocking for F#)

Resources