The Microsoft.CodeAnalysis.LanguageNames class has an entry for F#:
public static class LanguageNames
{
public const string CSharp = "C#";
public const string VisualBasic = "Visual Basic";
public const string FSharp = "F#";
}
This got me excited, I thought I could let my users also use F# just by toggling one property. Alas, it doesn't seem to be the case. There are Roslyn nuget packages for C# and VB.NET but nothing for F#.
Is support for analyzing and running F# code planned in Roslyn?
If not, what alternatives would you recommend for parsing and executing F#?
You can use F# Compiler Service.
The problem with F# on Roslyn is that F# has been self-hosted from the very start, while C# and VB compilers were written in C++ for a long time. By the time C# and VB finally grew up and got Roslyn, F# already had a well developed compiler with its own architecture. Worse, that architecture could not be easily followed in Roslyn (though I think they didn't even try), because it makes heavy use of language features not available in C# and VB.
Related
using Visual Studio 2017 I'm trying to compile the following F# snippet
type Foo() =
inherit System.Tuple<string,string>("","")
which fails telling me that I cannot inherit from sealed type. I inspected FSharp.Core and it's not sealed. I'm also convinced that this compiled with VS 2010.
Any ideas? Is this caused by extra F# metadata? I know that F# treats tuples in special way
As of F# 4.1, F# treats all System.Tuple instances as an F# tuple, which cannot be inherited from.
When F# 4.5 was announced, it was stated that:
The F# feature set is comprised of
[...]
The ability to produce IsByRefLike structs (examples of such structs: Span<'T>and ReadOnlySpan<'T>).
How to "produce" these types? I tried the [<IsByRefLike>] attribute but is was not found in .NET Standard 2.0.
The attribute is found in System.Runtime.CompilerServices
open System.Runtime.CompilerServices
[<Struct; IsByRefLike>]
type Apa =
{ A: Span<int>
B: int }
Phillip Carter talks about this in What's new in F# 4.5 (about 21 min. in).
It is available for .NET Core and .NET Framework, but not .NET Standard 2.0.
Starting with .NET SDK 6.0.200 (available in Visual Studio 2022 17.1), the F# compiler recognizes user-defined IsByRefLikeAttributes. The following code will transparently enable defining ref structs on .NET Standard 2.0 as well as later frameworks:
#if NETSTANDARD2_0
namespace System.Runtime.CompilerServices
open System
[<Sealed; AttributeUsage(AttributeTargets.Struct)>]
type IsByrefLikeAttribute() = inherit Attribute()
#endif
namespace MyLibrary
open System
open System.Runtime.CompilerServices
[<IsByRefLike>]
type MyRefStruct(span: Span<int>) = struct end
Technically, this is not an answer.
First, according to the specs, IsByRefLike is for the compiler, not for the developers to use: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.isbyreflikeattribute?view=netframework-4.7.2
Second, when we do want something from a compiler, then it is crucial that we do understand what we want from it. So a correct question could be: I need ABC because I need to do XYZ, where ABC would be something related to IsByRefLike and then XYZ would be something based on IsByRefLike. The question does not explain why IsByRefLike is needed.
I think that a minimalist approach should be always weighted in when considering which language features to use / not to use: do we really need some particular language feature to accomplish what we need? F# Option.bind comes to mind: if the function is a one-liner then Option.bind is great. However, if there is some tree of how to parse the result, then it might be better to do that explicitly without using Option.bind.
So the questions are:
Do you really need IsByRefLike?
If you think that you do, could you, please, post an example of where you actually do need it.
While trying to prove to a colleague that it's possible to use C++ classes from F#, I came up with the following proof of concept. The first snippet is the code he provided for the challenge, and the code snippet below is my implementation in F#.
namespace testapp {
struct trivial_foo {
int bar;
__declspec(dllexport) void set(int n) { bar = n; }
__declspec(dllexport) int get() { return bar; }
}
}
open System.Runtime.InteropServices
type TrivialFoo =
struct
val bar: int
new(_bar: int) = { bar = _bar }
end
[<DllImport("Win32Project2.dll", EntryPoint="?get#trivial_foo#testapp##QAEHXZ", CallingConvention = CallingConvention.ThisCall)>]
extern int trivial_foo_get(TrivialFoo& trivial_foo)
[<DllImport("Win32Project2.dll", EntryPoint="?set#trivial_foo#testapp##QAEXH#Z", CallingConvention = CallingConvention.ThisCall)>]
extern void trivial_foo_set(TrivialFoo& trivial_foo, int bar)
type TrivialFoo with
member this.Get() = trivial_foo_get(&this)
member this.Set(bar) = trivial_foo_set(&this, bar)
When debugged in Visual Studio or run as a standalone program, this works predictably: TrivialFoo.Get returns the value of bar and TrivialFoo.Set assigns to it. When run from F# Interactive however, TrivialFoo.Set will not set the field. I suspect it might have something to do with accessing managed memory from unmanaged code, but that doesn't explain why it only happens when using F# Interactive. Does anyone know what's going on here?
I don't think this proof of concept is a good proof of interoperability. You may be better off creating DLL export definitions from your C++ project and use the de-decorated names instead.
As a PoC: F# creates MSIL that fits in the CLI, so it can interoperate with any other CLI language out there. If that is not enough and you want native-to-net interop, consider using COM, or as mentioned above, DLL export definitions on your C++. I personally wouldn't try to interop with C++ class definitions the way you suggest here, there are way easier ways to do that.
Alternatively, just change your C++ project into a .NET C++ project and you can access the classes directly from F#, while still having the power of C++.
Naturally, you may still be wondering why the example doesn't run in FSI. You can see a hint of an answer by running the following:
> System.IO.Directory.GetCurrentDirectory();;
val it : string = "R:\TMP"
To fix this, you have a myriad of options:
copy Win32Project2.dll to that directory
add whatever path it is in to PATH
use an absolute path
use a compile-time constant
or use an environment variable (the path will be expanded)
dynamically locate the dll and dynamically bind to it (complex)
Copying is probably the easiest of these solutions.
Since FSI is meant to be a REPL, it may not be best tailored for this kind of tasks that require multiple projects, libraries or otherwise complex configurations. You may consider voting on this FSI request for support for #package to import NuGet packages, which could be used to ease such tasks.
The counterpart of a C++ struct in F# is not necessarily a struct. In C++, the only difference between classes and structs resides in their default access restrictions.
In F#, structs are used for value types, classes are used for reference types. One problem with value types is that they are meant to be used as immutable values, and temporary copies are often created silently.
The problem you are observing is consistent with that scenario. For some reason, F# interactive creates a copy of your struct and passes a reference to that. The C++ code then modifies the copy, leaving the original untouched.
If you switch to using a class, make sure you pin the instance before letting native code use it, or you can end up in a situation where the garbage collector moves it after the native code gets a reference to it.
If I use the F# Type Providers from the assembly FSharp.Data.TypeProviders 4.3.0.0, I am able to create types in a very simple F# library. I am then able to use those types without any dependency on the assembly FSharp.Data.TypeProviders. That is pretty sweet! Here is an example:
I created an F# library project called TryTypeProviders. I put this in the .fs:
module TryTypeProviders
type Northwind = Microsoft.FSharp.Data.TypeProviders.ODataService
I then am able to use the F# library from a C# project:
public static void Main()
{
var c = new TryTypeProviders.Northwind();
foreach (var cust in c.Customers)
Console.WriteLine("Customer is: " + cust.ContactName);
Console.ReadKey(true);
}
I haven't been able to find any working examples of how to create a type provider like this. The type providers in FSharpx.TypeProviders are not accessible from C#. My guess is that they are erased types and not generated types. I'm still a little fuzzy on which is which, but it is defined here as:
Generated types are real .NET types that get embedded into the assembly that uses the type provider (this is what the type providers that wrap code generation tools like sqlmetal use)
Erased types are simulated types which are represented by some other type when the code is compiled.
The samples from the F# 3.0 Sample Pack mentioned in the MSDN tutorial are not working for me. They build, but when I try to use them I get errors.
open Samples.FSharp.RegexTypeProvidertype PhoneNumberRegEx = CheckedRegexProvider< #"(?<AreaCode>^\d{3})-(?<PhoneNumber>\d{3}-\d{4}$)">
open Samples.FSharp.MiniCsvProvidertype csv = MiniCsvProvider<"a.csv">
It was last released in March of 2011 and my guess is that they don't yet reflect the final version of type providers that shipped with Visual Studio 2012.
F# Type Providers look like a great technology, but we need help building them. Any help is appreciated.
The reason why standard type providers (for OData, LINQ to SQL and WSDL) work with C# is that they generate real .NET types behind the cover. This is called generative type provider. In fact, they simply call the code generation tool that would be called if you were using these technologies from C# in a standard way. So, these type providers are just wrappers over some standard .NET tools.
Most of the providers that are newly written are written as erasing type providers. This means that they only generate "fake" types that tell the F# compiler what members can be called (etc.) but when the compiler compiles them, the "fake" types are replaced with some other code. This is the reason why you cannot see any types when you're using the library from C# - none of the types actually exist in the compiled code.
Unless you're wrapping existing code-generator, it is easier to write erased type provider and so most of the examples are written in this way. Erasing type providers have other beneftis - i.e. they can generate huge number of "fake" types without generating excessively big assemblies.
Anyway, there is a brief note "Providing Generated Types" in the MSDN tutorial, which has some hints on writing generative providers. However, I'd expect most of the new F# type providers to be written as erasing. It notes that you must have a real .NET assembly (with the generated types) and getting that is not simplified by the F# helpers for building type providers - so you'll need to emit the IL for the assembly or generate C#/F# code and compile that (i.e. using CodeDOM or Roslyn).
If one uses the F# Interactive Shell (FSI), the inferred expression type (signature) is printed to the console along with its value:
val it : int * string * float = (42, "Hello F#", 42.0)
How can I mimick the same behaviour in my own code, e.g. to get the inferred types as string for a F# expression?
I don't need to dynamically evaluate any F# expressions, the expressions are known in compile time and are part of my (static) F# code. I need this feature to be able to mimick the FSI output in LINQPad for my F# demos.
Using Unquote
Unquote has a facility for getting the F# signature of a type. Simply download the latest release and add a reference through LINQPad to Unquote.dll, then you can do e.g.
If you're interested, you can peak at the source code for the implementation of the FSharpName Type extension: https://github.com/SwensenSoftware/unquote/blob/2.1.0/Unquote/ExtraReflection.fs#L54
Using FsEye
Another neat approach would be to use LINQPad's beta Custom Visualizer API to embed FsEye into LINQPad (FsEye uses the same F# type signature printing algorithm as Unquote). This is also very simple, all you need to do is download LINQPad beta, download and reference FsEye.dll from the latest release of FsEye, then you can do e.g.
If you look at the F# compiler code and see how the --sig option is handled by the compiler I think that will get you what you're looking for. More about the --sig option and signatures here:
Signatures (F#)