I need to do some setup in a module that I wanted to accomplish by leveraging a do block. Strangely though, my do block never seems to get hit.
Even stranger, If I load the module code into fsi, it does get hit. Here is my example:
Main.fs
[<EntryPoint>]
let main args =
printfn "%b" TestNamespace.M.x
0
TestModule.fs
namespace TestNamespace
module M =
do
printfn "In do"
failwith "Error" // this is line 6
let x = true
When I run the compiled executable I get
>test.exe
true
Why didn't the exception get thrown? If I run the module in FSI by itself I get
In do
System.Exception: Error
at <StartupCode$FSI_0006>.$FSI_0006.main#() in C:\Projects\Personal2\Playground\fsscripts\fsscripts\TestModule.fs:line 6
Stopped due to error
So it got the exception.
I see that in the decompliation that the do initializers get rolled into a seperate class
namespace \u003CStartupCode\u0024fsscripts\u003E
{
internal static class \u0024Library1
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[CompilerGenerated]
[DebuggerNonUserCode]
internal static int init\u0040;
static \u0024Library1()
{
ExtraTopLevelOperators.PrintFormatLine<Unit>((PrintfFormat<Unit, TextWriter, Unit, Unit>) new PrintfFormat<Unit, TextWriter, Unit, Unit, Unit>("In do"));
Operators.FailWith<Unit>("Error");
bool x = M.x;
}
}
}
VS the actual module code:
namespace TestNamespace
{
[CompilationMapping(SourceConstructFlags.Module)]
public static class M
{
public static bool x
{
[DebuggerNonUserCode] get
{
return true;
}
}
}
}
So how do I make sure the do block actually gets executed?
--
Edit, given the above example counts as a simple constant expression so won't produce an observable initialization, why does the following also not work?
[<EntryPoint>]
let main args =
printfn "%d" (TestNamespace.M.x id 1)
0
namespace TestNamespace
module M =
do
printfn "In do"
failwith "Error"
let x f a = f a
This prints out 1 no problem.
Edit, after having re-read Tomas's comments its because a function is considered a constant expression.
For a good explanation of the problem, see the answer to this previous SO question. The important bit says:
the static initializer for the file is executed on first access of a value that has observable initialization
Now, "observable initialization" is somewhat tricky idea, but simple constant initialization definitely does not have observable initialization - that's why the do block is not executed. You can trick the compiler into thinking that there is some imperative action, for example by adding do ():
module M =
do
printfn "In do"
failwith "Error" // this is line 6
let x = (do ()); true
You can get the behavior you want, while maintaining the same public interface, with a class:
type M private () =
static do
printfn "In do"
failwith "Error"
static member val x = true
Here is a clean way to do it.
First, note that if you wanted the initialization code to run every time the function is called, you would do this:
module M =
let init () =
printfn "In init"
let x f a =
init ()
printfn "In x"
f a
So, if you want it to be called just once (static initialization), you can simply remove the () from both places:
module M =
let init =
printfn "In init"
let x f a =
init
printfn "In x"
f a
The nice thing is that you have documented your design that the init code will be called first. If you have several separate blocks of initialization code, it is clear which dependency you are relying on (although the first initialization will execute all such blocks of course).
UPDATE
Here is a version, which works in a Release build too. Not quite so clean, but almost:
module Init =
open System.Runtime.CompilerServices
[<MethodImpl(MethodImplOptions.NoInlining)>]
let call init = init
module M =
let init =
printfn "In init"
let x f a =
Init.call init
printfn "In x"
f a
Note that init is still a unit value, so Init.call is a non-inlined function that does nothing at all. So there is the overhead of a function call to nothing.
An alternative, which also works but seems a little strange is this:
module M =
let mutable init =
printfn "In init"
let x f a =
init <- init
printfn "In x"
f a
Can anyone improve on this?
Related
I'm trying to understand the reader monad transformer. I'm using FSharpPlus and try to compile the following sample which first reads something from the reader environment, then performs some async computation and finally combines both results:
open FSharpPlus
open FSharpPlus.Data
let sampleReader = monad {
let! value = ask
return value * 2
}
let sampleWorkflow = monad {
do! Async.Sleep 5000
return 4
}
let doWork = monad {
let! envValue = sampleReader
let! workValue = liftAsync sampleWorkflow
return envValue + workValue
}
ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"
With this I get a compilation error at the line where it says let! value = ask with the following totally unhelpful (at least for me) error message:
Type constraint mismatch when applying the default type 'obj' for a type inference variable. No overloads match for method 'op_GreaterGreaterEquals'.
Known return type: Async
Known type parameters: < obj , (int -> Async) >
It feels like I'm just missing some operator somewhere, but I can't figure it out.
Your code is correct, but F# type inference is not that smart in cases like this.
If you add a type annotation to sampleReader it will compile fine:
let sampleReader : ReaderT<int,Async<_>> = monad {
let! value = ask
return value * 2
}
// val sampleReader : FSharpPlus.Data.ReaderT<int,Async<int>> =
// ReaderT <fun:sampleReader#7>
Update:
After reading your comments.
If what you want is to make it generic, first of all your function has to be declared inline otherwise type constraints can't be applied:
let inline sampleReader = monad ...
But that takes you to the second problem: a constant can't be declared inline (actually there is a way but it's too complicated) only functions can.
So the easiest is to make it a function:
let inline sampleReader () = monad ...
And now the third problem the code doesn't compile :)
Here again, you can give type inference a minimal hint, just to say at the call site that you expect a ReaderT<_,_> will be enough:
let inline sampleReader () = monad {
let! value = ask
return value * 2
}
let sampleWorkflow = monad {
do! Async.Sleep 5000
return 4
}
let doWork = monad {
let! envValue = sampleReader () : ReaderT<_,_>
let! workValue = liftAsync sampleWorkflow
return envValue + workValue
}
ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"
Conclusion:
Defining a generic function is not that trivial task in F#.
If you look into the source of F#+ you'll see what I mean.
After running your example you'll see all the constraints being generated and you'll probably noted how the compile-time increased by making your function inline and generic.
These are all indications that we're pushing F# type system to the limits.
Although F#+ defines some ready-to-use generic functions, and these functions can sometimes be combined in such a way that you create your own generic functions, that's not the goal of the library, I mean you can but then you're on your own, in some scenarios like exploratory development it might make sense.
I'm trying to createa a base test class where I can setup common methods.
type BaseTest() =
member self.Assert (functionToEvaluate:bool, ?errorMessage:string) =
let a = fun () -> defaultArg errorMessage ""
match errorMessage with
| None -> NUnit.Framework.Assert.That(functionToEvaluate)
| _ -> NUnit.Framework.Assert.That(functionToEvaluate, a )
[<TestFixture>]
type MyTest () =
inherit BaseTest()
[<Test>]
member self.``test something``() =
let x = 1
self.Assert_( (x = 2))
// or
self.Assert_( (x = 2), "x value is not 2")
How to make the code "clean" (let a ... is horrible for me)
How to avoid using this/self in the derived class?
How can I write it like self.Assert(x=1) or even better just Assert(x=1) instead of self.Assert((x=1)) ?
What I want tot do (and I can do with C#) is this:
// in base test class
protected void Assert(bool test) => NUnit.Framework.Assert.That(test);
protected void Assert(bool test, string errorMessage) => NUnit.Framework.Assert.That(test, errorMessage);
// in test class
public void TestSomething() {
var x = 1
Assert(x==2)
// or
Assert(x==2, "x is not 2")
}
Your problem is that you're trying to translate the C# program into F# verbatim and expect it to look "nice" without realizing that the initial C# program is already full of C#-specific tricks that exist in order to make it look "nice". One example is the base class. Why is there a base class? Does it represent something? No, it doesn't: it's there for the sole purpose of avoiding a class name when calling these functions - i.e. you write Assert instead of SomeHelper.Assert. This stems out of the fact that in C# you can't have free-standing functions.
But in F# - you can!
let assrt x = NUnit.Framework.Assert.That(x)
let assrtWith x msg = NUnit.Framework.Assert.That(x, msg)
[<TestFixture>]
type SaleRepositoryTest () =
[<Test>]
member self.``test something``() =
let x = 1
assrt (x=2)
assrtWith (x=2) "x is not 2"
(Note that you can't use the name assert, because it's a keyword)
Also note that you generally don't need classes. The fact that in C# you can't do anything without them is a giant mistake that grows out of Java design, which was a misunderstanding of OO.
You can have free-standing functions. If I remember correctly, NUnit should be able to discover such tests just fine (though I can't verify right now):
let [<Test>] ``test something``() =
let x = 1
assrt (x=2)
assrtWith (x=2) "x is not 2"
Finally, I strongly recommend that you consider FsUnit. It can bind to NUnit (if you're locked into that) and provides a nice library of F#-idiomatic assertions.
I was trying to get a better understanding of the Functional way of coding, and just wrote a small program to print back the factorial of a user-entered number:
open System
let fact n = let rec factiter init acc =
if init = 0 then acc
else factiter (init - 1) init*acc
factiter n 1
let dropStrArr (argv: string []) = ignore argv
let factComp = Console.ReadLine >> Int32.Parse >> fact >> Console.WriteLine >> fun () -> 0
[<EntryPoint>]
let main argv = (dropStrArr >> factComp) argv
This worked fine but then I thought main could be defined purely by composition and tried:
let main = dropStrArr >> factComp
which I thought would work, but although it compiles, it simply exits immediately upon running.
There are different types in the two scenarios:
unit -> int
when main is defined with its argument, versus
(unit -> int)
when using composition.
I'm probably missing about the type system, so my question is why can't main just be defined via composition here?
The short answer is that writing point-free style in F# has consequences.
Functions which are partially applied, get compiled to FSharpFunc and are subsequently called with Invoke.
To illustrate:
let mul a b = a + b
let mul2 = mul 2 //point-free
let mul2P a = mul 2 a //pointed
mul2P look something like you'd expect (in equivalent C#)
static int mul2P(int a) { return mul(2, a); }
while mul2 becomes
class mul2Impl : FSharpFunc<int, int>
{
public int a;
mul2Impl(int a) { this.a = a; }
public override int Invoke(int b)
{
return mul(this.a, b);
}
}
So when you write let main argv it becomes a simple static method which just calls the other two FSharpFunc with
factComp.Invoke(dropStrArr.Invoke(argv));
But when you compose it, main becomes an FSharpFunc and there's no more a static main method as is required.
I have stucked with the unit testing. I have the following source code:
module SampleTest
open FsUnit
open NUnit.Framework
[<TestFixture>]
[<Category("Category name")>]
type DoSthTest() =
let mutable state = []
[<SetUp>]
member public x.``run before test``() =
state = []
[<Test>]
member x.``add item``() =
state <- List.append state [1]
state.Length |> should equal 1
In general it runs fine.... but without the [] function.
I got the following exception: Result Message: Invalid signature for SetUp or TearDown method: run before test
Does someone know the answer why ?
And the second question is: is it possible to write an unittest without type definition but with the SetUp also function working?
I mean sth like this:
module SampleTest
open FsUnit
open NUnit.Framework
let mutable state = []
[<SetUp>]
let ``run before test``() =
state = []
[<Test>]
let ``add item``() =
state <- List.append state [1]
state.Length |> should equal 1
again I got the same exception as before
In F#, mutable values are assigned using <- rather than =.
So your Setup method should look like:
[<SetUp>]
member public x.``run before test``() =
state <- []
which works fine.
For your second question, this layout works fine for me if you make the same change as above.
When I tried the console programming, I received unexpected result.
open System
let printSomeMessage =
printfn "Is this the F# BUG?"
[<EntryPoint>]
let main args =
if args.Length = 2 then
printSomeMessage
else
printfn "Args.Length is not two."
0
The printSomeMessage function was included in .cctor() function. Here is IL DASM result.
.method private specialname rtspecialname static
void .cctor() cil managed
{
// Code size 24 (0x18)
.maxstack 4
IL_0000: nop
IL_0001: ldstr "Is this the F# BUG\?"
IL_0006: newobj instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [mscorlib]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit>::.ctor(string)
IL_000b: call !!0 [FSharp.Core]Microsoft.FSharp.Core.ExtraTopLevelOperators::PrintFormatLine<class [FSharp.Core]Microsoft.FSharp.Core.Unit>(class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0,class [mscorlib]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
IL_0010: dup
IL_0011: stsfld class [FSharp.Core]Microsoft.FSharp.Core.Unit '<StartupCode$FSharpBugTest>'.$Program::printSomeMessage#3
IL_0016: pop
IL_0017: ret
} // end of method $Program::.cctor
So, its execution result is like this.
Is this the F# BUG?
Args.Length is not two.
Am I missing some grammar or F# characteristic? Or F# builder’s BUG?
No it's a bug in your code. You need to add parentheses after "printSomeMessage", otherwise printSomeMessage is a simple value rather than a function.
open System
let printSomeMessage() =
printfn "Is this the F# BUG?"
[<EntryPoint>]
let main args =
if args.Length = 2 then
printSomeMessage()
else
printfn "Args.Length is not two."
0
Simple values are initialized in the constructor of a module, so you see your code being called when the module is initialized. This is logical when you think about it, the normal case of simple values would be binding a string, integer, or other literal value to an identifier. You would expect this to happen a start up. i.e. the following will be bound at module start up:
let x = 1
let y = "my string"