Running async computation expression in FSI - f#

I'm trying to run this code in the FSI
#r #"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.1\ref\net6.0\System.Net.Http.dll"
async {
let httpClient = new System.Net.Http.HttpClient()
let! response = httpClient.GetAsync("") |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
return! response.Content.ReadAsStringAsync() |> Async.AwaitTask
}
and I get the following error
Error FS0193: The module/namespace 'System.Threading.Tasks' from
compilation unit 'System.Runtime' did not contain the namespace,
module or type 'Task`1'
However when I paste that code in a file in a project it compiles as expected.
What I'm missing?

I'm not sure why you are getting this error, but you should be able to reference standard .NET libraries without specifying the full path.
I tried creating an F# Script with the following and it worked perfectly fine (I'm still using F# 5, but I do not expect this would change in a new version):
#r #"System.Net.Http.dll"
async {
let httpClient = new System.Net.Http.HttpClient()
let! response = httpClient.GetAsync("http://tomasp.net") |> Async.AwaitTask
response.EnsureSuccessStatusCode() |> ignore
return! response.Content.ReadAsStringAsync() |> Async.AwaitTask
}
|> Async.RunSynchronously
I just had to add a real URL to download and Async.RunSynchronously to actually run the code.

Related

Why is Async.RunSynchronously hanging?

In my f# project, I'm calling a c# library that returns me a Task. When I try to do this in my f# project, nothing happens.
let str = getName() |> Async.AwaitTask |> Async.RunSynchronously
However, if I update my code to use an async workfolow, it doesn't hang anymore. What am I doing wrong when calling Async.RunSynchronously?
async {
let! str = getName() |> Async.AwaitTask
//Do something with str
}
In your second example you just build async workflow, but don't actually run it.
It is designed that way so you could define a complex workflow without running every async part of it immediately, but instead run the whole thing when it's good and ready.
In order to run it you need to call Async.RunSynchronously or Async.StartAsTask:
async {
let! str = getName() |> Async.AwaitTask
//Do something with str
} |> Async.RunSynchronously
Also, if you do need to work with Tasks, it's probably better to use TaskBuilder.fs instead of Async.

Error Installing a Windows Service (in F#)

my question is the following:
When I try to install my Windows Service I get the following error:
snippet:
...
No public installers with the RunInstallerAttribute.Yes attribute could be found in the <path to exe> assembly.
...
I follow this tutorial
I have one Program.fs file containing:
[<RunInstaller(true)>]
type public FSharpServiceInstaller() =
inherit Installer()
do
< some logic, doesn't really matter >
This should be sufficient, as a matter of fact, I don't even think I need to add the public keyword to the type definition. Installing this executable with InstallUtil.exe gives me the same error as installing it using the following code:
[<EntryPoint>]
let main args =
if Environment.UserInteractive then
let parameter = String.Concat(args);
match parameter with
| "-i" -> ManagedInstallerClass.InstallHelper [| Assembly.GetExecutingAssembly().Location |]
| "-u" -> ManagedInstallerClass.InstallHelper [| "/u"; Assembly.GetExecutingAssembly().Location |]
| _ -> printf "Not allowed!\n"
else
ServiceBase.Run [| new CreditToolsService() :> ServiceBase |];
0
I have tried running this script in PowerShell, cmd and Visual Studio CLI as both administrator and my normal account but I keep getting the same error. If anyone knows what I'm doing wrong I would really appreciate some help.
OK, so here goes...
I've looked at the code provided by user1758475 and just randomly started copy pasting solutions into an application. Don Symes's solution "just worked" and I finally figured out why: I did not (and he does) have a namespace declaration, in my source. Seems like this was the culprit! After I added the namespace the installer worked like a charm.
As Curt Nichols pointed out, the installer should not be in a module because a module effectively hides the type from the calling code.
Thank you for help in figuring this out.
For those of you who want to see a working example:
namespace FileWatcher
open System
open System.Reflection
open System.ComponentModel
open System.Configuration.Install
open System.ServiceProcess
open System.IO
open System.Configuration
type FileWatcherService() =
inherit ServiceBase(ServiceName = "FileWatcher")
let createEvent = fun (args: FileSystemEventArgs) ->
printf "%s has been %s\n" args.FullPath (args.ChangeType.ToString().ToLower())
|> ignore
override x.OnStart(args) =
let fsw = new FileSystemWatcher ()
fsw.Path <- "C:\TEMP"
fsw.NotifyFilter <- NotifyFilters.LastAccess ||| NotifyFilters.LastWrite ||| NotifyFilters.FileName ||| NotifyFilters.DirectoryName ||| NotifyFilters.CreationTime
fsw.Filter <- "*.txt"
fsw.EnableRaisingEvents <- true
fsw.IncludeSubdirectories <- true
fsw.Created.Add(createEvent)
override x.OnStop() =
printf "Stopping the FileWatcher service"
[<RunInstaller(true)>]
type public FSharpServiceInstaller() =
inherit Installer()
do
// Specify properties of the hosting process
new ServiceProcessInstaller
(Account = ServiceAccount.LocalSystem)
|> base.Installers.Add |> ignore
// Specify properties of the service running inside the process
new ServiceInstaller
( DisplayName = "AAA FileWatcher Service",
ServiceName = "AAAFileWatcherService",
StartType = ServiceStartMode.Automatic )
|> base.Installers.Add |> ignore
module Program =
[<EntryPoint>]
let main args =
printf "starting the application...\n"
if Environment.UserInteractive then
let parameter = String.Concat(args);
match parameter with
| "-i" -> ManagedInstallerClass.InstallHelper [| Assembly.GetExecutingAssembly().Location |]
| "-u" -> ManagedInstallerClass.InstallHelper [| "/u"; Assembly.GetExecutingAssembly().Location |]
| _ -> printf "Not allowed!\n"
else
ServiceBase.Run [| new FileWatcherService() :> ServiceBase |];
0
Working live production example at https://github.com/zbilbo/TB4TG/blob/master/TourneyBot.Service/Installer.fs
Think it needs to be installed with InstallUtil.exe though.
Possibly not the finest moment in coding, but that specific service code is from Don Syme more or less: http://blogs.msdn.com/b/dsyme/archive/2011/05/31/a-simple-windows-service-template-for-f.aspx, so it is probably fine, but the rest of the "surrounding" code on that repository may not be idiomatic ;-)
Don Symes blog also explains a lot more so it should be easily to adept it to your needs. It also links to a Win Service Template on VS Gallery: http://blogs.msdn.com/b/mcsuksoldev/archive/2011/05/31/f-windows-application-template-for-windows-service.aspx

Running c# async method in f# workflow

I am trying to get the below code to work in a F# async workflow, but I am getting the error "Unexpected symbol '}' in expression". I am fairly new to both F# and async in general. What am I missing here.
let someFunction (req : HttpRequestMesssage) a b =
// code
async{
let! readToProvider =
req.Content.ReadAsMultipartAsync(provider)
|> Async.AwaitIAsyncResult
} |> Async.RunSynchronously
req.CreateResponse(HttpStatusCode.OK)
I worry that my previous answer wasn't quite what you want. What I supplied just got you through the compile error. But one thing about it, is that it does not run asynchronously. Task.Wait and Async.RunSynchronously will both block the running thread until the operation is complete.
If you want to actually be async, i.e. not blocking, you have to put the entire method, or at least the last part of it, into the async block, such that you're actually returning an async op to the caller. So the answer would be
let someFunction (req : HttpRequestMesssage) a b =
async {
let! readToProvider = (req.Content.ReadAsMultipartAsync provider) |> Async.AwaitIAsyncResult
return req.CreateResponse HttpStatusCode.OK
}
This option returns not the response, but an Async<Response>. So now the caller can decide how to run it, either blocking or truly asynchronously.
This way if you're using a web server that handles asynchronous requests, then you can simply connect this function to an endpoint (probably converting the Async to a Task at the point of connection, since most .net async web servers are written from C# perspective) and it'll run asynchronously without blocking a thread. Or if you're calling it from another async op you can do do! someFunction ... and it'll run asynchronously. But if the caller doesn't care and just wants to run synchronously, it can do someFunction ... |> Async.RunSynchronously. So you get more flexibility there. And you can always define let someFunctionSync ... = someFunction ... |> Async.RunSynchronously if that's the more common use case.
I'd recommend going this way unless you really want to enforce blocking.
You're doing it right. You're only getting the error because you're ending your async block with a let! expression. Change it to return!, or do! ... |> Async.Ignore and you'll be good.
Blocks in F# (neither workflows nor regular code blocks) should not end with let.
Of course if all you're really doing in the workflow is that one call, you don't need the workflow block at all (you never really should need to write a block for a single call). Just do
req.Content.ReadAsMultipartAsync provider
|> Async.AwaitIAsyncResult
|> Async.Ignore
|> Async.RunSynchronously
req.CreateResponse HttpStatusCode.OK
Or for that matter, just use the built in Tasks Wait, which does the same thing as Async.RunSynchronously:
(req.Content.ReadAsMultipartAsync provider).Wait()

Unit Testing F# Code Throws Null Reference Exception, why?

I'm using the NUnit testing technique suggested in the yet to be released book "F# Deep Dives Version 12" (Sec. 2.2 "Adding Tests")
The code below executes fine compiled or interactive with MEMOIZE defined/undefined. However, executing the unit test from the GUI NUnit works fine with MEMOIZE undefined, but it fails with a "Null Reference Exception" when MEMOIZE is defined. Notice memorize() uses a Closure. I'm suspecting the exception is happening because some initialization code generated by the compiler is not getting executed when NUnit starts up. What do you think?
open System
open System.Collections.Generic
open NUnit.Framework
open FsUnit
let memoize (f: 'T -> 'U) =
let t = new Dictionary<'T, 'U>(HashIdentity.Structural)
fun n ->
if t.ContainsKey n then t.[n]
else let res = f n
t.Add(n, res)
res
//TODO: Insure J>0 & K>0 & J<K
let isMult =
#if MEMOIZE
memoize (fun (j,k) -> k % j = 0)
#else
(fun (j,k) -> k % j = 0)
#endif
type ``Given the isMult function``() =
[<TestCase(3,1,false)>]
[<TestCase(3,2,false)>]
[<TestCase(3,3,true)>]
[<TestCase(5,10,true)>]
[<TestCase(3,15,true)>]
[<TestCase(5,13,false)>]
[<TestCase(5,15,true)>]
member t.``the result is calculated correctly``(j, k, expected) =
let actual = isMult (j,k)
actual |> should equal expected
UPDATE:
The standalone NUnit application is version 2.6.3.13283.
"FinnNk" gave me an idea! I installed Nuget package "NUnitTestAdapter". Now I can test directly within VS 2013. No surprises, though. I get all tests 'green' when MEMORIZE is undefined and all tests 'red' when it is defined.
The exception is still the same: "Null Reference Exception". However, now that it executes in the IDE, I can have the debugger stop on the exception. All I can determine so far at the breakpoint is that it needs the symbols from:
C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.pdb
I installed the new VS 2015 Preview Edition. Nothing different happens in that environment. Now that .NET Framework is open source, maybe I can zero the debugger precisely on the problem with the source code for "mscorlib".
Are you running your NUnit tests in multiple threads? Normal dictionary is not thread-safe so weird things can happen. How about if you use ConcurrentDictionary, will it give the same result?
let memoize (f: 'T -> 'U) =
let t = System.Collections.Concurrent.ConcurrentDictionary<'T, 'U>()
fun n -> t.GetOrAdd(n, f)

WebSharper force a message passing call to be asynchronous

Knowing an RPC call to a server method that returns unit is a message passing call, I want to force the call to be asynchronous and be able to fire the next server call only after the first one has gone to the server.
Server code:
[<Rpc>]
let FirstCall value =
printfn "%s" value
async.Zero()
[<Rpc>]
let SecondCall() =
"test"
Client code:
|>! OnClick (fun _ _ -> async {
do! Server.FirstCall "test"
do Server.SecondCall() |> ignore
} |> Async.Start)
This seems to crash on the client since returning unit, replacing the server and client code to:
[<Rpc>]
let FirstCall value =
printfn "%s" value
async { return () }
let! _ = Server.FirstCall "test"
Didn't fix the problem, while the following did:
[<Rpc>]
let FirstCall value =
printfn "%s" value
async { return "" }
let! _ = Server.FirstCall "test"
Is there another way to force a message passing call to be asynchronous instead?
This is most definitely a bug. I added it here:
https://bugs.intellifactory.com/websharper/show_bug.cgi?id=468
Your approach is completely legit. Your workaround is also probably the best for now, e.g. instead of returning Async<unit> return Async<int> with a zero and ignore it.
We are busy with preparing the 2.4 release due next week and the fix will make it there. Thanks!
Also, in 2.4 we'll be dropping synchronous calls, so you will have to use Async throughout for RPC, as discussed in https://bugs.intellifactory.com/websharper/show_bug.cgi?id=467 -- primarily motivated by new targets (Android and WP7) that do not support sync AJAX.

Resources