I'm trying to create a connection to open a database over ODBC. I cannot figure out how to execute an objects member functions. The code:
let DbConnection = new System.Data.Odbc.OdbcConnection()
DbConnection.open
The errors I get are: Missing qualification after '.'
or sometimes: unexpected identifier in implementation file
Does anybody know what is wrong with my syntax?
I suppose you wanted something like this:
let dbConnection = new System.Data.Odbc.OdbcConnection()
dbConnection.Open()
The problems are:
F# is case sensitive so you need Open rather than open (also open is a language keyword, so if you wanted to use it as a name, you'd have to write ``open`` - the double back-tick is a way to refer to reserved names)
Open is a function, so if you want to call it you need to give it an argument. You can treat it as a function value too and write, say, let f = dbConnection.Open
I also changed your naming to use camelCase for variables, which is the standard F# way
Related
(previously "using type provider from C#")
This should be easy to answer, and the underlying issue has be asked before, but I'm slightly confused about it, having dug into it, there's something I'm missing.
The Xml type provider in
https://fsharp.github.io/FSharp.Data/library/XmlProvider.html
is erased?
so the types in it can't be used from another assembly?
"Erasing Type Providers produce types that can only be consumed in the assembly or project they are generated from" (says the MS docs).
but if I define in one assembly this
module Xml
type Xml = FSharp.Data.XmlProvider<"""
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>""">
let foo () = Xml.Parse ("")
and in another assembly, reference the above assembly and go...
let x = Xml.foo ()
let s = x.From
this compiles...so if the MS docs are correct, the provider must be generative?
if I implement the same code in C#,
var x = Xml.foo();
var y = x.From;
then this code doesnt compiler
'XmlElement' does not contain a definition for 'From' and no accessible extension method 'From' accepting a first argument of type 'XmlElement'
which seems to imply its erased...or I need to do something else (I've included the same dependencies).
My hunch is its erased, but then how does the F# assembly compile?
(I want it to be generative to use from C# and VB).
As far as I can tell by looking at the source code, all the types provided by FSharp.Data are erased.
The XmlProvider type is defined by this line at https://github.com/fsharp/FSharp.Data/blob/master/src/Xml/XmlProvider.fs#L26
let xmlProvTy = ProvidedTypeDefinition(asm, ns, "XmlProvider", None, hideObjectMethods=true, nonNullable=true)
As you can see, the instantiation of the ProvidedTypeDefinition type does not include isErased=false.
The ProvidedTypeDefinition type has two constructors. See the signatures at https://github.com/fsprojects/FSharp.TypeProviders.SDK/blob/master/src/ProvidedTypes.fsi#L268-L275
Both constructors have an implementation including this line, which means that the provide type is erased by default:
let isErased = defaultArg isErased true
Without redesigning the XmlProvider, the only way you can consume it from C# is to wrap it in an F# class that exposes C#-friendly types. Not sure if that would result in a better design than just avoiding the type provider altogether.
Assuming by "erased" you mean being removed from the FSharp.Data Library? then XMLProvider its probably not removed from it because I just tried your code and it works in F# project (as you have also noticed).
Now about the C# project the thing is TypeProviders are a Fsharp specific thing, whatever magic F# compiler is doing to type check xml and access the From xml-node here is probably only exclusive to F#.
The type of you variable x here is FSharp.Data.Runtime.BaseTypes.XmlElement, looking at the documentation here and the source code here.
There doesn't seem to be any way of accessing the xml nodes in an OOP way. Its probably not meant to be accessed that way either. If you want to parse xml and read its nodes in C# there are plenty of other ways for that. One being XmlReader in System.Xml
I just started to study F# and accidentally wrote this binding
let List = 1
Now when I try to obtain List methods such as 'filter' I get this error
error FS0039: The field, constructor or member 'filter' is not defined.
Of course using method with full type name like Microsoft.FSharp.Collections.List.filter is still working.
I'm wondering why it is possible to use type name as identifier in F# and how I can set back name List to type List from Microsoft.FSharp.Collections.
When I tried to reassign like this
type List = Microsoft.FSharp.Collections.List<'T>
I get
Error FS0039: The type parameter 'T is not defined.
Thank you!
In F# you can redefine almost everything and shadow existing definitions. This applies to both types (well actually types have a different behavior regarding shadowing, they shadow their values as you open the namespaces) and values but not interchangeably since values and type (and also modules) can somehow coexist at the same time in the scope. The compiler will do his best to find out which one is.
You are not forced to, but it's a common good practice in F# not to use let bindings in uppercase.
Regarding your second question, you are using a type parameter in the right side which doesn't exist in the left side of the assignment, it should be:
type List<'T> = Microsoft.FSharp.Collections.List<'T>
But notice that filter doesn't belong to the type. It's rather defined in the List module.
You should just rename your let binding from List to something sensible - as Gustavo mentioned, your definition is shadowing the core List module from F# and there is no way to use List to refer both to your integer and to the module. Shadowing core functions will make your code pretty confusing. It's also a good idea to use camelCase for let bindings, but that's a matter of taste.
If you insist on shadowing List, then you won't be able to call List.filter using List.filter. If you wanted something shorter, you could define module alias:
module FsList = Microsoft.FSharp.Collections.List
Note that your attempt to do something similar with List<'T> does not do the same thing, because functions such as filter are in a module named List rather than being static members of the type. With this, you can call filter using FsList.filter.
Instead of importing a whole module, is there a way to open specific functions in another module? Something like:
open TestFuncs with [myTestFuncInOtherModule]
As you can see in the docs, the open keyword doesn't actually load a module or namespace. It just allows you to refer to elements in that module/namespace without refering to their fully qualified name.
Being so, when you use open you're just making it easier to call the functions in that module, not actually importing/loading them in memory, so the short answer for this question is that using the open keyword you can't do this to only one function.
You can achieve the same thing easily with a let binding, though:
let f = TestFuncs.myTestFuncInOtherModule
It's not currently possible, but I've actually put in a request for this to be added to the language. If you want to see this added in F# 4.0, one thing you could do is go vote for that request.
Another good workaround, that hasn't been mentioned yet by other answers, is to "pseudo-open" modules: assign a short name to modules whose contents you want to use. Like so:
module TP = Microsoft.FSharp.Data.TypeProviders
type mySchema = TP.SqlDataConnection<"...">
let db = mySchema.GetDataContext()
This gives you the convenience of not having to type the whole module name every time you want to reference its contents, but you maintain control of your namespace: this way there's no chance of accidental name collisions when you update a module to a new version and it adds new names.
You can refer to particular functions in another module using full function name ModuleName.funcName:
module One =
let square x = x * x
module Two =
let anothersquare x = One.square x
I'm working on a demo where I can show how easy it is to use the SqlClient type provider in a F# library and then consume it from C#, I think this might be one way to get things into projects. Since many people using C# want to use interfaces etc. I thought I show them that it is possible to combine everything, but I don't get the expected result. I have the following F# code which works as expected in F#:
module Orders =
[<Literal>]
let connStr = """XXXXXXXXXXXX"""
type OrdersPerEmployee = SqlCommandProvider<"OrdersPerEmployee.sql", connStr>
open Orders
type IOrdersRepo =
abstract member getOrdersPerEmployee : int -> OrdersPerEmployee.Record seq
type OrdersRepo() =
interface IOrdersRepo with
member this.getOrdersPerEmployee(employeeId) =
let query = new OrdersPerEmployee()
query.Execute(employeeId)
But when I try to use it from my C# app I don't get that the getOrdersPerEmployee returns an enumerable of records, instead it returns an enumerable of objects:
IOrdersRepo repo = new OrdersRepo();
var data = repo.getOrdersPerEmployee(4).ToList();
data.ForEach(y => Console.WriteLine(y));
Console.ReadLine();
In the code above I expceted to get intellisense on y in the lambda, but nothing. Any ideas?
Because SqlCommandProvider is erasing types kind. It means types it generates can be consumed only from F#. Contrary, SqlEnumProvider from the same FSharp.Data.SqlClient library is generated types kind and can be consumed by C# (via proxy project). It works especially nice when CLIEnum=true.
For a deeper discussion on erased vs generated types see
How do I create an F# Type Provider that can be used from C#?
Is there any way to open a namespace of types provided by a type provider implementation? I ask, because some of the generated type name paths are very long and ugly in code. I have used type abbreviations to alleviate this, but obviously this is a manual process. The F# open keyword does not support this. Is there another way? Update: as pointed out in the answer and comments this is wrong. You can open a type provided namespace. I had not realised I was looking at deeply nested types, not a namespace.
This is tricky - parameterized type providers (like the standard SQL providers or the F# Data providers for XML and JSON) need to put all types that they generate (representing tables, XML nodes, etc) inside the main generated type. So all types that you might want to use are hidden somewhere as nested types of the main type (with parameters specified).
This makes sense - if you use the type provider with multiple parameters, the types generated for each configuration have to be separate.
As #kvb points out, you cannot open a nested type, but you can use type aliases to make this a bit less painful. For example, using F# Data, I can define an alias R that lets me access all the generated domain types with just two additional characters:
#r #"..\packages\FSharp.Data.1.1.10\lib\net40\FSharp.Data.dll"
open FSharp.Data
type RssFeed = XmlProvider<"http://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml">
type R = RssFeed.DomainTypes
let printTitle (itm:R.Item) = printfn "%A" itm.Title