F# function changes type when compiled with standalone switch and referenced from another project - f#

In a Visual Studio project for an F# library I have defined a function as
let inline Estimate (s : ^a seq) (f : float) (w : int) : float * float = ..
The type of Estimate is
val Estimate : s:seq<'a> -> f:float -> w:int -> float*float
Calling Estimate from a script within that project works as expected.
Now if I compile the project with the --standalone switch and reference the output DLL from another project, Estimate is shown to be
Estimate<'a,'a>(s: Collections.Generic.IEnumerabls<'a>, f: float, w:int) : float*float
i.e. it some reason now takes tuple arguments.
Thus the following does not work
let q, p = EstimationQuality.Estimate x f 1 // This value is not a function and cannot be applied
but calling it with tuple parameters works fine
let q, p = EstimationQuality.Estimate (x, f, 1) // No problem.
What's wrong here? Is it a bug in the compiler?
EDIT:
Digging a little deeper, it appears that the problem is linked with the use of LanguagePrimitives.GenericZero.
While the problem actually compiles with the tuple parameter call, i get a runtime error when Estimate is called.
An unhandled exception of type 'System.TypeInitializationException'
occurred in LibraryTest.dll
Additional information: The type initializer for
'GenericZeroDynamicImplTable`1' threw an exception.

Compiling an F# DLL which is intended to be used from F#, with the standalone switch is not a good idea.
Why? Because all the F# metadata is lost since the whole set of F# types are included in your DLL so those types get a different identity from the types of the F# application that call your DLL or fsi.
The caller assembly uses the types in Fsharp.Core.dll which now are not the same as the ones used in your standalone compiled DLL.
That's why you see the tupled arguments, as seen from C# which doesn't understand F# metadata at all.
Generic inline functions using static constraints break as well since they need the metadata to inline at the call site.
Compiling also the caller assembly as standalone would make things worse, then you will have 3 sets of Fsharp types with different identities.
I think the standalone switch is fine when used only in the 'end-user' application.

Related

Am I using TextLoader wrong when running the ML.Net Iris demo in F#?

I am new to F#/.NET and I am trying to run the F# example provided in the accepted answer of How to translate the intro ML.Net demo to F#? with the ML.NET library, using F# on Visual Studio, using Microsoft.ML (0.2.0).
When building it I get the error error FS0039: The type 'TextLoader' is not defined.
To avoid this, I added the line
open Microsoft.ML.Data
to the source.
Then, however, the line
pipeline.Add(new TextLoader<IrisData>(dataPath,separator = ","))
triggers:
error FS0033: The non-generic type 'Microsoft.ML.Data.TextLoader' does not expect any type arguments, but here is given 1 type argument(s)
Changing to:
pipeline.Add(new TextLoader(dataPath,separator = ","))
yields:
error FS0495: The object constructor 'TextLoader' has no argument or settable return property 'separator'. The required signature is TextLoader(filePath: string) : TextLoader.
Changing to:
pipeline.Add(new TextLoader(dataPath))
makes the build successful, but the code fails when running with
ArgumentOutOfRangeException: Column #1 not found in the dataset (it only has 1 columns), I assume because the comma separator is not correctly picked up (incidentally, you can find and inspect the iris dataset at https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data).
Also
pipeline.Add(new TextLoader(dataPath).CreateFrom<IrisData>(separator: ','))
won't work.
I understand that there have been changes in TextLoader recently (see e.g. https://github.com/dotnet/machinelearning/issues/332), can somebody point me to what I am doing wrong?
F# just has a bit of a different syntax that can take some getting used to. It doesn't use the new keyword to instantiate a new class and to use named parameters it uses the = instead of : that you would in C#.
So for this line in C#:
pipeline.Add(new TextLoader(dataPath).CreateFrom<IrisData>(separator: ','))
It would be this in F#:
pipeline.Add(TextLoader(dataPath).CreateFrom<IrisData>(separator=','))

Release build fails when types leak from transitive dependency, Debug is fine

I'm trying to explain a weird F# compiler behavior between Release and Debug configurations regarding transitive dependencies. I will use Newtonsoft.Json package as the base dependency here, because that's the furthest I managed to get in pinpointing the issue, and it makes the example a bit less abstract.
Let's create a library project called SerializerProject, referencing Newtonsoft.Json via paket. In this project there is only one module:
module Serializer =
open System.IO
open System.Text
open Newtonsoft.Json
type OptionConverter() =
inherit JsonConverter()
(* only the signature is important, the implementation
and other methods are not interesting *)
override x.WriteJson(writer: JsonWriter, value: obj, serializer: JsonSerializer) =
(* ... *)
let fromJson<'a> (bytes: byte []): 'a =
let s = Encoding.UTF8.GetString(bytes)
use sr = new StringReader(s)
use jr = new JsonTextReader(sr)
let serializer = JsonSerializer()
serializer.Converters.Add(OptionConverter())
serializer.Deserialize<'a>(jr)
Now let's create a second project in the same solution and reference SerializerProject via project reference. I'd like to use fromJson in my new project, that's why I referenced SerializerProject in the first place.
module MyModule =
open Serializer
(* This is here just so we reference the fromJson function *)
let deserializer bytes = fromJson bytes
This is the minimal example to reproduce the behavior.
Now when I build the solution in Debug configuration, everything compiles and works fine. But when I switch to Release, the compilation fails in the second project, in MyModule in the deserializer definition. The exact error message is this:
The type referenced through 'Newtonsoft.Json.JsonWriter' is defined in an assembly that is not referenced. You must add a reference to assembly 'Newtonsoft.Json'
I'm using Visual Studio 2015 Update 3, F# tools (fsc, fsi) show version 14.0.23413.0.
It kind of makes sense, because it's reading metadata of the SerializerProject and finds that public OptionConverter type leaks the type JsonWriter on its public WriteJson method (as well as other types and other methods, but this one is encountered first), but what makes me wonder is why this works in Debug mode and is only a problem in the Release mode.
What kind of extra operations does the compiler do that affect this?
Why is this not a problem in Debug build when the type defined in Newtonsoft.Json really leaks transitively into the second project?
As suggested in the comments I tried referencing Newtonsoft.Json and decompiling the second assembly with ILSpy to see whether inlining turned on by compiler optimizations is to blame here, but even in Release configuration the second assembly looks like this:
call !!0 [SerializerProject]Serializer::fromJson<!a>(uint8[])
The fromJson function has not been inlined to expose the JsonWriter type directly, so there seem to be something more subtle going on.
This isn't a blocking issue, I just made the converter types private as I don't want to use them from the outside anyway, but I'd like to dig deeper in F# compiler inner workings.

F# NUnit test sets value as null

I have 2 files: Asm.fs, AsmTest.fs
Asm.fs
namespace Assembler
[<Measure>] type ln
[<Measure>] type col
[<Measure>] type port
type Addr = int<ln> * int<col> * int<port>
module Asm =
let emptyAddr : Addr = (-1<ln>, -1<col>, -1<port>)
AsmTest.fs
module Assembler.Tests
[<Test>]
let someTest() =
let x = Asm.emptyAddr
When I debug someTest() code, I get that x = null, what am I doing wrong?
P.S. files in the different projects visual studio projects. Asm.fs in the Assembler project and AsmTest.fs in the AssemblerTest project.
I found an interesting behavior. My problem will be resolved, if I add some file (even empty) to the Assembler project. Can anyone explain this behavior?
The debugger sometimes has issues showing the correct values. For me however, having exactly the same Asm.fs and AsmTest.fs like this:
module Assembler.Tests
open NUnit.Framework
[<Test>]
let someTest() =
let x = Asm.emptyAddr
Assert.IsNotNull x
the test passes and if I set a breakpoint at the assertion, x is correctly shown as {(-1, -1, -1)}
As the code that you show does not compile as it is (Block following this let is unfinished. in someTest), could you try my test above and/or show your complete test method?
Using your code I can reproduce the behaviour. If I set my projects to console applications, my test will fail as well. So, it seems that for console projects, not having any other file or an [<EntryPoint>] surprisingly skips the initialization of module values. For me, the compiler at least issues a warning Main module of program is empty where I use x in the test. The solution to the problem is therefore:
make sure you treat warnings as errors
have an [<EntryPoint>] for console applications
use library projects for libraries

Compile with standalone flag gives compilation errors in client code

I'm attempting to compile Zero29 with the --standalone compiler flag. The project itself compiles fine, but I have a unit test project that exercises some code in the Zero29 project, even though it's an executable program (.exe).
Everything works fine without the --standalone compilation flag.
However, when I add the --standalone compilation flag to the Zero29 project, the Zero29 project compiles fine, but in the unit test project, the compiler complains about this Discriminated Union defined in the Zero29 project:
namespace Ploeh.ZeroToNine
open System
open Ploeh.ZeroToNine.Versioning
type Arg =
| Assign of Version
| AssignRank of Rank * int
| Increment of Rank
| ListVersions
| ShowHelp
| Unknown of string list
The unit test project directly references the Zero29 project:
Zero29.UnitTests --references--> Zero29 (where --standalone is added)
When I attempt to compile the entire solution, the Zero29 project compiles with the --standalone flag, but then compilation of Zero29.UnitTests fails. There are several errors, but they are all the same, so here's a single example:
error FS0039: The value or constructor 'Assign' is not defined
Which points to the third line of this code:
let ParseAssignVersionReturnsCorrectResult(version : string) =
let actual = [| "-a"; version |] |> Args.Parse
verify <# [Assign(Version version)] = (actual |> Seq.toList) #>
The strange thing is that while the compiler complains about Assign in the third line of this code snippet, it doesn't complain about the use of Args.Parse, even though it's defined in the same code file as the Arg Discriminated Union.
Why does it do that, and how can I resolve this issue?
(I've attempted to distil the problem here, but the links I've provided point to the actual code files on GitHub, if more information is required.)
Libraries compiled with the --standalone switch cannot expose any F# datatypes. This is, for one, expressly stated in Pickering (2007), p. 210. In your case, a discriminated union is one of these prohibited types. The fact that the file is an executable changes nothing here: it becomes a library the moment you attempt to use it as one.
There have been also multiple reports (for example, here and here) that even libraries compiled with --standalone behave, quoting one of these sources, “funky.” It would be safe to say that the use of this switch should perhaps be limited to stand-alone executables only (and they cannot pretend to be a library even when under unit tests).
Pickering R. (2007). Foundations of F#. Apress.

ComImport in F#

I'm trying to translate some code from C# to F#, specifically the code to create a shortcut, from here: http://vbaccelerator.com/home/NET/Code/Libraries/Shell_Projects/Creating_and_Modifying_Shortcuts/ShellLink_Code_zip_ShellLink/ShellLink_cs.asp
The code in C# reads:
[GuidAttribute("00021401-0000-0000-C000-000000000046")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[ComImportAttribute()]
private class CShellLink{}
Which I translated to F# as:
[<GuidAttribute("00021401-0000-0000-C000-000000000046")>]
[<ClassInterfaceAttribute(ClassInterfaceType.None)>]
[<ComImportAttribute()>]
type CShellLink() = class end
Unfortunately, when I switch to the F# implementation, I get a runtime error of: "Method with non-zero RVA in an Import". This seems to be the same error as reported here: http://social.msdn.microsoft.com/Forums/en-US/fsharpgeneral/thread/dada2004-5218-4089-8918-eed2464bbbcd
Is there any workaround? I'm trying to port the application to only use F#, so if this can't be written in F# that project is going to have to be rethought.
this looks like a limitation in F# compiler: it cannot define existing COM classes using ComImportAttribute, it only works for interfaces. Can you use this as workaround?
let shellLink =
let ty = System.Type.GetTypeFromCLSID (System.Guid "00021401-0000-0000-C000-000000000046")
Activator.CreateInstance ty

Resources