To test CoreRT, run the following command:
dotnet publish -r <RID> -c <Configuration>
for example:
dotnet publish -r win-x64 -c release
For more information see restore-and-publish-your-app
To test .Net Native:
-
Remove the block on F# code by editing the line containing FSharpRule in
"C:\Program Files (x86)\Microsoft SDKs\UWPNuGetPackages\runtime.win10-x64.Microsoft.Net.Native.Compiler\2.1.8\tools\x64\ilc\tools\GatekeeperConfig.xml"
(or appropriate variation of that path - see the ILT0005 error in step 2 for where to look) to:<FSharpRule on="false">
-
Run the UWP project in release mode.
We have identified several issues while testing. The main code tested was the F# cheatsheet, a production F# Xamarin.Forms app, and tail recursion and tail recursion and nested generics.
The code demonstrating the issues found is here: https://github.com/FoggyFinder/FSharpCoreRtTest/tree/MainIssues . Please test further and give your results and conclusisions here.
.NET Native: works by default.
Documentation: Serialization and Metadata. But if you use NewtonSoft.Json you don't have to edit your rd.file (at least currently) because the specification for this library is included by default.
CoreRT: requires rd.xml.
This example (WebAPI) shows how to use NewtonSoft.Json. The basic rd file:
<Assembly Name="Newtonsoft.Json">
<Type Name="Newtonsoft.Json.Serialization.ObjectConstructor`1[[System.Object,System.Private.CoreLib]]" Dynamic="Required All" />
</Assembly>
<Assembly Name="System.Linq.Expressions">
<Type Name="System.Linq.Expressions.ExpressionCreator`1[[Newtonsoft.Json.Serialization.ObjectConstructor`1[[System.Object,System.Private.CoreLib]],Newtonsoft.Json]]" Dynamic="Required All" />
<Type Name="System.Linq.Expressions.ExpressionCreator`1[[System.Func`2[[System.Object,System.Private.CoreLib],[System.Object,System.Private.CoreLib]],System.Private.CoreLib]]" Dynamic="Required All" />
</Assembly>
<Assembly Name="Namespace" Dynamic="Required All" />
where Namespace
is namespace in your app which contains types for serialization.
It may be not enough. For example if type contains F# list you have to add this line:
<Assembly Name="FSharp.Core" Dynamic="Required All">
<Type Name="Microsoft.FSharp.Collections.ListModule" Dynamic="Required All">
<Method Name="OfSeq" Dynamic="Required">
<GenericArgument Name="System.Object, System.Private.CoreLib" />
</Method>
</Type>
</Assembly>
.NET Native: just override .ToString for custom types and use String.Format
instead of sprintf
.
It is possible that rd.xml will enable sprintf on primitive types but we have not managed to get this working yet.
CoreRT: partially fixed by appropriate rd.xml for primitive types. See Some FSharp.Core constructs don't run on CoreRT #4954. The issue results from CoreRT/.NET Native restricting MakeGenericType
/MakeGenericMethod
. RD.XML (a file passed to the CoreRT compiler) can be used to tell the compiler that particular code needs to be generated (even though it statically looks like it isn't needed). If there's a reasonable bound on what MakeGenericType/MakeGenericMethod gets called with, RD.XML is all that's needed to make this work. See the simple example in this repo.
Related link: Dynamic programming differences
This is useful for LINQ queries from F#.
Workaround: use an alternative to LINQ (e.g. SQL), or use LINQ from C#.
E.g. the following code fails:
let expr =
<@ System.Func<int,int>(fun i -> i + 1) @>
|> Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter.QuotationToExpression
It appears that .Net Native ignores tail instructions, while CoreRT implements them. Lack of support for .tail calls on UWP does not break typical F# code. It is therefore recommended not to create code in UWP that is heavily dependent on tail call optimization.
This needs more investigation on CoreRT.
type ChannelChangedHandler = delegate of obj * int -> unit
type C() =
let channelChanged = new Event<ChannelChangedHandler,_>()
[<CLIEvent>]
member self.ChannelChanged = channelChanged.Publish
member self.ChangeChannel(n) = channelChanged.Trigger(self,n)
let test() =
let c = C()
let h1 = ChannelChangedHandler(fun _ ch -> Console.WriteLine("Channel = {0}", ch))
c.ChannelChanged.AddHandler(h1)
c.ChangeChannel(3)
Unhandled Exception: System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property. ---> EETypeRva:0x0063BF58: This object cannot be invoked because it was metadata-enabled for browsing only: 'FSLibrary.ChannelChangedHandler.Invoke(System.Object,System.Int32)' For more information, please visit http://go.microsoft.com/fwlink/?LinkID=616867
at CoreRTConsole!<BaseAddress>+0x13edd
at CoreRTConsole!<BaseAddress>+0x8d667
at CoreRTConsole!<BaseAddress>+0x88fc6
at CoreRTConsole!<BaseAddress>+0x89388
at CoreRTConsole!<BaseAddress>+0x850fb
at CoreRTConsole!<BaseAddress>+0x21b7dd
at CoreRTConsole!<BaseAddress>+0x1d8a90
--- End of inner exception stack trace ---
at CoreRTConsole!<BaseAddress>+0x1d8b38
at CoreRTConsole!<BaseAddress>+0x1d8964
at Microsoft.FSharp.Control.FSharpEvent`2.Trigger(Object, TArgs) + 0x23
at Program.main(String[]) + 0xd
at CoreRTConsole!<BaseAddress>+0x270f6a