Notes and Guidance on FSharp.Core
This technical guide discusses the FSharp.Core library. Please help improve this guide by editing it and submitting a pull-request.
- Do not bundle FSharp.Core with a library
- Do bundle FSharp.Core with an application
- FSharp.Core is binary compatible
- C# projects referencing F# projects need an FSharp.Core reference
- The FSharp.Core NuGet Package
- FSharp.Core version numbers
- General Guidance
- Application v. Library v. Script
- Do not bundle FSharp.Core with a library
- Do deploy FSharp.Core as part of your application
- Do not assume FSharp.Core is in the GAC
- Do not assume any specific version of FSharp.Core is in the GAC, even if it is on your machine
- What to do for error “Could not load file or assembly FSharp.Core, Version=22.214.171.124” or similar
- FSharp.Core is binary compatible
- Libraries target lower versions of FSharp.Core
- Applications target higher versions of FSharp.Core
- Use Binding Redirects for Applications
- FSharp.Core in F# Interactive
- FSharp.Core and static linking
- FSharp.Core in components using FSharp.Compiler.Service
- Making PCL-portable libraries
FSharp.CoreEntries in Project Files
TargetFSharpCoreVersionin all F# projects
Private=Truewhen referencing FSharp.Core in applications
- A C# project referencing an F# DLL or NuGet package may need to also have a reference to FSharp.Core.dll
- Examples of how project files reference FSharp.Core and the F# .targets file
- FSharp.Core in Xamarin apps
- Missing FSharp.Core References in .fsproj Project Files
- The FSharp.Core NuGet package
- FSharp.Core version numbers
Application v. Library v. Script
Each project is either an application or a library.
Examples of application are
.exeproject or a
.dllproject that is a test project, addin, website, or an app.
Libraries are just ordinary
.dllcomponents (excluding those above which are applications).
Scripts are not projects, just
.fsxfiles, possibly referring to other files using
#loadand Libraries using
Do not bundle FSharp.Core with a library
Do not include a copy of FSharp.Core with your library or package. If you do, you will create havoc for users of your library.
The decision about which
FSharp.Core a library binds to is up to the application hosting of the library.
The library and/or library package can place constraints on this, but it doesn’t decide it.
Especially, do not include FSharp.Core in the
lib folder of a NuGet package.
Do deploy FSharp.Core as part of your application
For applications, FSharp.Core is normally part of the application itself (so-called “xcopy deploy” of FSharp.Core).
To achieve this, you normally use
<Private>true</Private> in your project file. In Visual Studio this is equivalent to setting the
CopyLocal property to
true properties for the
FSharp.Core.dll will normally appear in the
bin output folder for your application. For example:
Directory of ...\ConsoleApplication3\bin\Debug 18/04/2015 13:20 5,632 ConsoleApplication3.exe 18/04/2015 13:20 187 ConsoleApplication3.exe.config 14/10/2014 12:12 1,400,472 FSharp.Core.dll
Do not assume FSharp.Core is in the GAC
In compiled applications, you should never assume that FSharp.Core is in the GAC (“Global Assembly Cache”). Instead, you should deploy the appropriate FSharp.Core as part of your application.
Do not assume any specific version of FSharp.Core is in the GAC, even if it is on your machine
Once again, do not rely on FSharp.Core being in the GAC. For applications (see above),
<Private>true</Private> for the FSharp.Core reference in the project file (see below). In the Visual Studio IDE this is equivalent to setting the
CopyLocal property to
true properties for the
On some installations of F#, some versions of FSharp.Core are added to the GAC. Do not rely on these being present in production code being deployed off your machine.
There are some exceptions to this.
Standard installations of F# tools on Linux and Mac on Mono also install the latest FSharp.Core into the GAC. They also add machine-wide binding redirects for that component. That means that, for those machines, the latest installed FSharp.Core will be used by applications.
Some installations of F# on Windows Visual Studio do install FSharp.Core into the GAC.
- VS2010 installs FSharp.Core 126.96.36.199 only
- VS2012 installs FSharp.Core 188.8.131.52 only
- VS2013 installs FSharp.Core 184.108.40.206 only
- VS2015 installs FSharp.Core 220.127.116.11 only
- VS2017 doesn’t install FSharp.Core to the GAC
If you can assume a particular version of Visual Studio (e.g. in a dev/test situation), then you may be able to assume FSharp.Core is in the GAC.
But it is best to avoid this assumption and instead make sure an appropriate FSharp.Core is deployed as part of your application.
What to do for error “Could not load file or assembly FSharp.Core, Version=18.104.22.168” or similar
The normal way to resolve this is to deploy FSharp.Core as part of your application, see above. See also this stackoverflow question and many other similar answers on the web.
Earlier versions of F# (Visual F# 2.0) had a “runtime redistributable” you could install on target machines. This model is now no longer used and instead you deploy FSharp.Core as part of your application.
FSharp.Core is binary compatible
FSharp.Core is binary compatible across versions of the F# language. For example, FSharp.Core
22.214.171.124 (F# 2.0) is binary compatible with
126.96.36.199 (F# 3.0),
188.8.131.52 (F# 3.1),
184.108.40.206 (F# 4.0) and so on.
Likewise, FSharp.Core is binary compatible from “portable” profiles to actual runtime implementations. For example, FSharp.Core
220.127.116.11 (a portable profiles for F# 3.1, see the table below) is binary compatible
with the runtime implementation assembly
18.104.22.168 (F# 3.1) and
22.214.171.124 (F# 4.0).
Binary compatibility means that a component built for X can instead bind to Y at runtime. It doesn’t mean that Y behaves the same as X (some bug fixes may have been made, and Y may have more functionality than X).
Libraries target lower versions of FSharp.Core
F# ecosystem libraries should generally target the earliest, most portable version of FSharp.Core feasible.
If your library is part of an ecosystem, it can be helpful to target the earliest, most widespread language version and the earliest (4.0+) and most portable profiles of the .NET Framework feasible.
In some cases you might also like to attempt to make your component into a PCL Portable component. This is covered below.
For personal libraries, or libraries that are effectively part of an application, the choice is yours, just target the latest language version and the framework you’re using in your application.
Applications target higher versions of FSharp.Core
F# applications should generally use the highest language version and the most platform-specific version of FSharp.Core.
Generally, when writing an application, you want to use the highest version of FSharp.Core available for the platform you are targeting.
If your application in being developed by people using multiple versions of F# tooling (common in open source working) you may need to target a lower version of the language and a correspondingly earlier version of FSharp.Core.
Use Binding Redirects for Applications
If applications use library components that reference an earlier FSharp.Core, then they may need binding redirects to specify that those libraries should bind to the actual FSharp.Core used as part of the application.
app.config is used to specify binding redirects. It is normal to redirect all lower versions of FSharp.Core to
the version actually being used. For example, to redirect all versions of FSharp.Core up to 126.96.36.199 to use 188.8.131.52:
<dependentAssembly> <assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-184.108.40.206" newVersion="220.127.116.11" /> </dependentAssembly>
This is located in a section of the app.config like this:
<?xml version="1.0" encoding="utf-8" ?> <configuration> ... <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="FSharp.Core" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-18.104.22.168" newVersion="22.214.171.124" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
Application project files should normally also specify
AutoGenerateBindingRedirects which allows tooling to
help keep the binding redirects up-to-date, see below.
FSharp.Core in F# Interactive
F# Interactive (
fsi.exe) always references and uses the FSharp.Core of the corresponding tool chain, as follows:
- F# 3.0 –> 126.96.36.199
- F# 3.1 –> 188.8.131.52
- F# 4.0 –> 184.108.40.206
- F# 4.1 –> 220.127.116.11
F# Interactive can load PCL assemblies that reference compatible FSharp.Core
FSharp.Core and static linking
The ILMerge tool and the F# compiler both allow static linking of assemblies including static linking of FSharp.Core. This can be useful to build a single standalone file for a tool.
However, these options must be used with caution.
Only use this option for applications, not libraries. If it’s not a .EXE (or a library that is effectively an application) then don’t even try using this option.
Prior to F# 4.0, static linking could not be used for F# assemblies which use quotation literals. Even for F# 4.0 static linking can only be used for F# assemblies compiled with F# 4.0 and referencing at least FSharp.Core 18.104.22.168 (or a corresponding portable profile for FSharp.Core).
Searching on stackoverflow reveals further guidance on this topic.
FSharp.Core in components using FSharp.Compiler.Service
If your application of component uses FSharp.Compiler.Service, see this guide. This scenario is more complicated because FSharp.Core is used both to run your script or application, and is referenced during compilation.
Likewise, if you have a script or library using FSharp.Formatting, then beware that is using FSharp.Compiler.Service. For scripts that is normally OK because they are processed using F# Interactive, and the default FSharp.Core is used. If you have an application using FSharp.Formatting as a component then see the guide linked above.
Making PCL-portable libraries
PCL portable libraries target a subset of .NET functionality and can be used in many different circumstances. F# PCL portable libraries target a special FSharp.Core which is binary compatible with the final FSharp.Core used for an application (see above). The FSharp.Core numbers for PCL profiles are listed later in this guide.
In some cases you might like to make your own libraries that are PCL Portable components. Below is a table of the recommended profiles to target depending on the minimal version of F# you can assume your team or other users of your library are using.
|Version||Framework||Recommended PCL Profile|
|F# 2.0||.NET 4.0+||n/a|
|F# 3.0||.NET 4.0+||Profile47 (net45+sl5+netcore45+MonoAndroid1+MonoTouch1)|
|F# 3.1||.NET 4.0+||Profile47 (net45+sl5+netcore45+MonoAndroid1+MonoTouch1)|
|F# 3.1.2||.NET 4.0+||Profile47 (net45+sl5+netcore45+MonoAndroid1+MonoTouch1)|
|F# 4.0||.NET 4.5+||Profile259 (portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1) or Profile47|
From the above, it can be seen that targeting F# 3.0 or 3.1 and Profile47 is a good choice for open source libraries. (In Visual Studio 2013, this choice is labeled as “Portable Library (.NET 4.0, Windows Store, Silverlight 5.0)” though the generated libraries can also be used with Xamarin Android and Xamarin iOS.)
You should always reference FSharp.Core via the FSharp.Core nuget package when preparing PCL libraries, since this will select the correct corresponding FSharp.Core version, including on Linux and OSX.
To convert an existing project to a portable profile, it may well be easiest to edit the project file by hand. See the project file fragments later in this guide.
See this bug for why profiles 7,78,259 are not recommended when using F# 3.1, even though the tooling allows their creation and consumption.
FSharp.Core Entries in Project Files
By default, Visual Studio, Xamarin Studio and other tools generate well-formed F#
.fsproj project files which
reference FSharp.Core in an appropriate way for the purpose that the component serves. However, when using F# in
advanced ways (and especially in cross-platform and portable scenarios) it can be useful to understand the
appropriate project file entries. Additionally, in some F# projects the project file may have been edited by hand.
In these cases it becomes essential to ensure you are referencing FSharp.Core properly.
TargetFSharpCoreVersion in all F# projects
It is normal for all F# components to define the property TargetFSharpCoreVersion:
Later in the file
FSharp.Core will be mentioned in the actual
FSharp.Core reference, e.g.
... Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
or other variations on this.
AutoGenerateBindingRedirects=true in applications
It is normal for applications to use AutoGenerateBindingRedirects in their
This helps keep your binding redirects up-to-date when using Visual Studio and other tools that understand this property.
Private=True when referencing FSharp.Core in applications
Applications should use
Private=true in their FSharp.Core reference.
This is means
FSharp.Core.dll is copied to the target directory and can be found at runtime, see above.
Libraries do not need to use this.
A C# project referencing an F# DLL or NuGet package may need to also have a reference to FSharp.Core.dll
A C# project referencing an F# DLL or NuGet package may need to also have a reference to FSharp.Core.dll. This reference must currently be managed explicitly unless you refer to the NuGet package for FSharp.Core (see below). Using the appropriate reference text below is recommended.
Examples of how project files reference FSharp.Core and the F# .targets file
- .NET 4.0, 4.5, 4.5.1, 4.5.2 etc.. For F# components restricted to run on .NET 4.x, it is normal to reference non-portable FSharp.Core using the following (adjust the FSharp.Core version number appropriately):
<PropertyGroup> ... <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFSharpCoreVersion>22.214.171.124</TargetFSharpCoreVersion> ... </PropertyGroup> ... <ItemGroup> ... <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <Private>True</Private> </Reference> ... </ItemGroup> ... <PropertyGroup> <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath> </PropertyGroup> <Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
- PCL libraries. For F# portable PCL library components, it is normal to use the following text in the project file:
<PropertyGroup> ... <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkProfile>Profile78</TargetFrameworkProfile> <TargetProfile>netcore</TargetProfile> <TargetFSharpCoreVersion>126.96.36.199</TargetFSharpCoreVersion> ... </PropertyGroup> <ItemGroup> ... <Reference Include="FSharp.Core"> <Name>FSharp.Core</Name> <AssemblyName>FSharp.Core.dll</AssemblyName> <HintPath>$(MSBuildExtensionsPath32)\..\Reference Assemblies\Microsoft\FSharp\.NETCore\$(TargetFSharpCoreVersion)\FSharp.Core.dll</HintPath> </Reference> ... </ItemGroup> ... <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.Portable.FSharp.Targets" />
- Legacy PCL libraries. “Legacy” portable projects (profile 47) use
<PropertyGroup> ... <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> <TargetFrameworkProfile>Profile47</TargetFrameworkProfile> <TargetFSharpCoreVersion>188.8.131.52</TargetFSharpCoreVersion> ... </PropertyGroup> <ItemGroup> ... <Reference Include="FSharp.Core"> <Name>FSharp.Core</Name> <AssemblyName>FSharp.Core.dll</AssemblyName> <HintPath>$(MSBuildExtensionsPath32)\..\Reference Assemblies\Microsoft\FSharp\.NETPortable\$(TargetFSharpCoreVersion)\FSharp.Core.dll</HintPath> </Reference> ... </ItemGroup> ... <PropertyGroup> <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.Portable.FSharp.Targets</FSharpTargetsPath> </PropertyGroup> <Import Project="$(FSharpTargetsPath)" />
For components created with earlier F# tooling (e.g. Visual Studio 2012 or before), project files may use different reference text. These should generally be adjusted to use the formulations above, this may be done automatically by some tooling.
FSharp.Core in Xamarin apps
FSharp.Core is referenced as a Private/CopyLocal component in Xamarin apps for mobile devices. This reference is handled via
the normal Xamarin framework install locations i.e
/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/Xamarin.iOS/FSharp.Core.dll for Xamarin.iOS.
Missing FSharp.Core References in .fsproj Project Files
This section deals with how .fsproj project files that are missing an FSharp.Core reference are handled by different tooling.
If there is no
FSharp.Core reference in your fsproj, Visual Studio will assume an environment of “latest”, i.e. whatever version it’s currently running against. The generated compiler command line has no
FSharp.Core ref in it. The compiler will make the same fallback decision (assume reference to whatever version it is running) in order to determine the FSharp.Core version it will use as target. So for F# 4.0 it will be as if you referenced
FSharp.Core, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a.
But that’s where it gets tricky - the compiler actually has to resolve that version information to an assembly location. On Windows it will use MSBuild to do that resolution. With a regular install of the VF# tools on Windows, a reg key is written at
HKLM\SOFTWARE\Wow6432Node\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx\F# 4.0 Core Assemblies which points to the appropriate
%ProgramFiles(x86)%\Reference Assemblies\... location which should be used for compile-time resolution. MSBuild looks at AssemblyFoldersEx when determining candidate resolution paths (that’s a general MSBuild extensibility mechanism). If you whack that key, then
FSharp.Core winds up being resolved from the GAC, and that fails due to missing optdata/sigdata.
So… for Visual Studio, the latest version of
FSharp.Core can be obtained by inspecting the currently-executing code in Visual Studio, e.g.
fsi.exe. But finding a suitable copy that sits next to optdata/sigdata requires outside assistance - either a reg key, a call to MSBuild or some other pointer to get you to
The FSharp.Core NuGet package
FSharp.Core is also available as a NuGet package.
It is now very common to reference FSharp.Core via this package, and this may help greatly in some situations, e.g. developing portable libraries on Linux. You may also find you reference this package via a transitive NuGet dependency.
You should reference the FSharp.Core NuGet package when build PCL components, especially when working across multiple platforms.
At the time of writing, the relevant versions of the NuGet packages were as follows (check the package versions and descriptions for latest information).
The NuGet package includes all of the FSharp.Core redistributables from Visual F#. In addition, they include assemblies for MonoAndroid and MonoTouch built from The F# Open Edition Repository and other extra “builds” of FSharp.Core for Xamarin-related portable profiles.
If you make the FSharp.Core NuGet package a dependency of your own NuGet package, you will induce a transitive dependency that NuGet package for any users of your package,. This forces them to manage their FSharp.Core dependency more manually via NuGet commands rather than via Visual Studio or other IDE tooling. Use caution if necessary.
FSharp.Core version numbers
Main .NET Framework DLLs (used at runtime for applications on .NET 4.x):
|F# 2.0||.NET 4.0+||220.127.116.11|
|F# 3.0||.NET 4.0+||18.104.22.168|
|F# 3.1||.NET 4.0+||22.214.171.124|
|F# 4.0||.NET 4.5+||126.96.36.199|
|F# 4.1||.NET 4.5+||188.8.131.52|
Portable PCL profiles (used at compile-time for portable libraries, can also be used at runtime when testing or as part of Windows Phone/Store apps):
Mobile and other platform DLLs (used at compile-time and runtime for applications, provided by Xamarin)
|F# 3.1||MonoTouch, MonoDroid||184.108.40.206|
|F# 4.0||MonoTouch, MonoDroid||220.127.116.11|
.NET 2.0/3.5 DLLs (used at compile-time or runtime for applications, do not support F# 3.1+ constructs)
Version numbering system for recent and future releases
|Target Framework||F# 3.0 / VS 2012||F# 3.1 / VS 2013||3.1.1, 3.1.2 updates||F# 4.0 / VS 2015||F# X.Y / VS vFuture|
|.NET 2||18.104.22.168||22.214.171.124 [frozen]||126.96.36.199 [frozen]||188.8.131.52 [frozen]|
The F# Core Engineering Group is a working group associated with The F# Software Foundation.
Visit our website and please continue all the great work on core F# engineering!
First Published: 18 April 2015
The F# Core Engineering group
- Starting an F# + .NET Core Development Group
- The F# Language and Core Library RFC Process
- F# Compiler Technical Overview
- Notes and Guidance on FSharp.Core
- Recommended Guidelines for F# Projects, Packages and Namespaces
- Online meeting notes, 18 September 2014
- Contributing to the F# Language and Compiler
- Some Recent F# Core Engineering Highlights
- Introducing the F# Core Engineering Group
- Online meeting notes, 02 July 2013
- The F# Core Engineering Group Goals, Remit and Activities