Using F#, if I partially apply a function like this:
let sleep x = Async.Sleep x |> Async.RunSynchronously
let log date message= printfn "%s %s" date message
let getDate = DateTime.Now.ToString()
let logg = log getDate
logg "First"
sleep 1000
logg "Second"
sleep 1000
Prints:
30/11/2018 19:54:25 First
30/11/2018 19:54:25 Second
The call to getDate appears to be translated to an actual date in the new partial function it produces. Why is this, and is it possible to make it call getDate on each call to this log function?
so I have to declare all methods in the chain that use getDate with () after them, and call them accordingly to make them reevaluate when called, otherwise they become fixed variables. Is that right?
I'd put it the other way around: let always binds a value, but in let getDate () or let sleep x case those values happen to be functions; they are basically equivalent to
let sleep = fun x -> Async.Sleep x |> Async.RunSynchronously
let getDate = fun () -> DateTime.Now.ToString()
Of course, this isn't really what happens if you examine bytecode, but "morally" it does. With these definitions
let sleep = fun x -> Async.Sleep x |> Async.RunSynchronously
let log = fun date message -> printfn "%s %s" date message
let getDate = fun () -> DateTime.Now.ToString()
let logg = fun x -> log (getDate ()) x
logg "First"
sleep 1000
logg "Second"
sleep 1000
you'll get the same output:
30/11/2018 19:59:40 First
30/11/2018 19:59:41 Second
Related
Hi I have a question I have quick program that will do something in every 10 seconds and it looks simple:
[<EntryPoint>]
let main argv =
let timer = new Timer(float 10000)
let OnTimedEvent (frameId:uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
while(true) do
let keyStroke = Console.ReadKey()
if keyStroke.Key.Equals(ConsoleKey.Enter) then
let frameId = 1u
timer.AutoReset <- true
timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
timer.Elapsed.AddHandler(OnTimedEvent frameId)
timer.Start();
else
printfn "%c pressed" keyStroke.KeyChar
0
Problem is I can not properly remove handler as it is, If i press enter once it starts and gives me one message every 10 sec so thi is what I am aiming for. But if i press enter 3 times it increments and gives me 3 messeges and so on, but i only want one.
Another thing is that if I remove parameter from it it works perfectly, so i suppose problem is with parameter. Any solutions for this?
The problem with your current implementation is that every call to OnTimedEvent returns a new instance of the ElapsedEventHandler. When you call it as follows:
timer.Elapsed.RemoveHandler(OnTimedEvent frameId)
you are removing a new handler that has not previously been registerd and so nothing actually happens. When you change your code to add/remove the same handler, then you are always using the same instance:
let frameId = 1u
let timedHandler = new ElapsedEventHandler (fun obj args ->
printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
timer.Elapsed.RemoveHandler(timedHandler)
timer.Elapsed.AddHandler(timedHandler)
Now you do not have a good way of passing the frameId to your event handler. In your code, frameId is always 1u and so it's hard to see what you actually want, but you could make it mutable:
let mutable frameId = 1u
let timedHandler = new ElapsedEventHandler (fun obj args ->
printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
frameId <- 2u
timer.Elapsed.RemoveHandler(timedHandler)
timer.Elapsed.AddHandler(timedHandler)
That said, it's not really clear what you are trying to do and perhaps there is an altogether different way of doing what you want.
A completely different approach would be to use MailboxProcessor that keeps the current frameId and handles two types of messages - one triggered every 10 seconds by a timer and one that can be used to change the frame ID:
type Message =
| Tick
| ChangeFrameId of uint32
let agent = MailboxProcessor.Start(fun inbox ->
let rec run frameId = async {
let! msg = inbox.Receive()
match msg with
| ChangeFrameId newId ->
return! run newId
| Tick ->
printfn "DATE: %A FRAME: %i" DateTime.Now frameId
return! run frameId }
run 1u)
let timer = new Timer(float 10000, AutoReset = true)
timer.Elapsed.Add(fun _ -> agent.Post(Tick))
timer.Start()
agent.Post(ChangeFrameId 2u)
This code refactors what you have to store the handler so that it can be removed.
open System
open System.Timers
[<EntryPoint>]
let main argv =
let timer = new Timer(float 10000, AutoReset = true)
let onTimedEvent (frameId: uint32) : ElapsedEventHandler = new ElapsedEventHandler (fun obj args -> printfn "DATE: %A FRAME: %i" DateTime.Now frameId)
let rec readKey frameId =
let handler = onTimedEvent frameId
timer.Elapsed.AddHandler(handler)
timer.Start()
let keyStroke = Console.ReadKey()
timer.Stop()
timer.Elapsed.RemoveHandler(handler)
printfn "%c pressed" keyStroke.KeyChar
let nextFrameId =
if keyStroke.Key.Equals(ConsoleKey.Enter) then
frameId + 1u
else
frameId
readKey(nextFrameId)
readKey(1u)
0
There may be better ways to accomplish what you are after, but this answers your question.
I have the following code to throttle the call of httpRequestStringThrottled once a second. However, the function is called without any delay. Did I miss anything?
let createThrottler (delay: TimeSpan) =
MailboxProcessor.Start(fun inbox ->
let rec loop (lastCallTime: DateTime option) =
async {
let! (chan: AsyncReplyChannel<_>) = inbox.Receive()
let sleepTime =
match lastCallTime with
| None -> 0
| Some time -> int((time - DateTime.Now + delay).TotalMilliseconds)
if sleepTime > 0 then
do! Async.Sleep sleepTime
let lastCallTime = DateTime.Now
chan.Reply()
return! loop(Some lastCallTime)
}
loop None)
let httpThrottler = createThrottler (TimeSpan.FromMilliseconds 1000.)
let httpRequestStringThrottled url =
async {
do! httpThrottler.PostAndAsyncReply id
return! httpRequestStringAsync url
}
// Test
[0..100] |> Seq.map (fun _ ->
let html = httpRequestStringThrottled "..." |> Async.RunSynchronize
html)
Your code is actually working fine, as far as I can tell, in F# interactive. You're just missing the calls to actually evaluate the Asyncs you create in your sequence:
// Test
[0..100] |> Seq.map (fun _ ->
let html = httpRequestStringThrottled "..."
html) |> Async.Parallel |> Async.RunSynchronously
Your code creates a sequence of 101 Async values. These are created immediately, but they do not evaluate immediately. They are only evaluated when you call something like Async.Start or Async.RunSynchronously. In your case, they will correctly be blocked for up to 1 second based on the time of the previous call, but that block only occurs during the evaluation of the Async, so you have to force them to evaluate to see the effect.
To test your code, I wrote a mock version of httpRequestStringAsync that prints the time when it's actually invoked.
let httpRequestStringAsync url =
async {
printfn "Requesting Html # %A..." DateTime.Now
do! Async.Sleep(50)
return "html"
}
Then, when running the modified version of your test above, I get the following output:
Requesting Html # 8/2/2018 11:15:17 AM...
Requesting Html # 8/2/2018 11:15:18 AM...
Requesting Html # 8/2/2018 11:15:19 AM...
Requesting Html # 8/2/2018 11:15:20 AM...
Requesting Html # 8/2/2018 11:15:21 AM...
Background.
I am trying to figure out MailboxProcessor. The idea is to use it as a some kind of state machine and pass arguments around between the states and then quit. Some parts are going to have async communication so I made a Sleep there.
It's a console application, making a Post does nothing because main thread quits and kills everything behind it. I am making a PostAndReply in main.
Also, I have tried without
let sleepWorkflow = async
, doesn't make any difference.
Questions.
(I am probably doing something wrong)
Go24 is not async. Changing RunSynchronously to StartImmediate makes no visible difference. The end should be somewhere below GetMe instead. At the same time Done is printed after Fetch. Isn't the control supposed t be returned to the main thread on sleep?
Go24, wait
go24 1, end
Fetch 1
Done
GetMe
...
Run time is terrible slow. Without delay in Fetch it's about 10s (stopwatch). I thought F# threads are lightweight and should use threadpool.
According to debugger it takes appr 1s to create every and it looks like real threads.
Also, changing to [1..100] will "pause" the program for 100s, according to ProcessExplorer 100 threads are created during that time and only then everything is printed. I would actually prefer fewer threads and slow increase.
Code.
Program.fs
[<EntryPoint>]
let main argv =
let a = Mailbox.MessageBasedCounter.DoGo24 1
let a = Mailbox.MessageBasedCounter.DoFetch 1
let b = Mailbox.MessageBasedCounter.GetMe
let task i = async {
//Mailbox.MessageBasedCounter.DoGo24 1
let a = Mailbox.MessageBasedCounter.DoFetch i
return a
}
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let x =
[1..10]
|> Seq.map task
|> Async.Parallel
|> Async.RunSynchronously
stopWatch.Stop()
printfn "%f" stopWatch.Elapsed.TotalMilliseconds
printfn "a: %A" a
printfn "b: %A" b
printfn "x: %A" x
0 // return an integer exit code
Mailbox.fs
module Mailbox
#nowarn "40"
type parserMsg =
| Go24 of int
| Done
| Fetch of int * AsyncReplyChannel<string>
| GetMe of AsyncReplyChannel<string>
type MessageBasedCounter () =
/// Create the agent
static let agent = MailboxProcessor.Start(fun inbox ->
// the message processing function
let rec messageLoop() = async{
let! msg = inbox.Receive()
match msg with
| Go24 n ->
let sleepWorkflow = async{
printfn "Go24, wait"
do! Async.Sleep 4000
MessageBasedCounter.DoDone() // POST Done.
printfn "go24 %d, end" n
return! messageLoop()
}
Async.RunSynchronously sleepWorkflow
| Fetch (i, repl) ->
let sync = async{
printfn "Fetch %d" i
do! Async.Sleep 1000
repl.Reply( "Reply Fetch " + i.ToString() ) // Reply to the caller
return! messageLoop()
}
Async.RunSynchronously sync
| GetMe (repl) ->
let sync = async{
printfn "GetMe"
repl.Reply( "GetMe" ) // Reply to the caller
return! messageLoop()
}
Async.RunSynchronously sync
| Done ->
let sync = async{
printfn "Done"
return! messageLoop()
}
Async.RunSynchronously sync
}
// start the loop
messageLoop()
)
// public interface to hide the implementation
static member DoDone () = agent.Post( Done )
static member DoGo24 (i:int) = agent.Post( Go24(i) )
static member DoFetch (i:int) = agent.PostAndReply( fun reply -> Fetch(i, reply) )
static member GetMe = agent.PostAndReply( GetMe )
I'm not necessarily sure that this is the main problem, but the nested asyncs and Async.RunSynchrously in the agent code look suspicious.
You do not need to create a nested async - you can just call asynchronous operations in the body of the match clauses directly:
// the message processing function
let rec messageLoop() = async{
let! msg = inbox.Receive()
match msg with
| Go24 n ->
printfn "Go24, wait"
do! Async.Sleep 4000
MessageBasedCounter.DoDone()
printfn "go24 %d, end" n
return! messageLoop()
| Fetch (i, repl) ->
(...)
Aside from that, it is important to understand that the agent has exactly one instance of the body computation running. So, if you block the body of the agent, all other operations will be queued.
If you want to start some task (like the synchronous operations) in the background and resume the agent immediately, you can use Async.Start inside the body (but be sure to call the main loop recursively in the main part of the body):
| Go24 n ->
// Create work item that will run in the background
let work = async {
printfn "Go24, wait"
do! Async.Sleep 4000
MessageBasedCounter.DoDone()
printfn "go24 %d, end" n }
// Queue the work in a thread pool to be processed
Async.Start(work)
// Continue the message loop, waiting for other messages
return! messageLoop()
I am using F# mailBoxProcessor to asynchronously process messages received from multiple network ends.
The code works as expected until I added function call getTreasuryYield after inbox.receive().
It gets stuck every time at inbox.receive() after running for a few seconds.
GetTreasuryYield is a quite slow method since it involves database and IO operations, but I
still do not understand how it gets stuck.
Any HELP will be appreciated.
let start rvSetting (agent:Agent<Message>) messageSelector=
try
TIBCO.Rendezvous.Environment.Open()
let _transport = new NetTransport(rvSetting.rvService, rvSetting.rvNetwork, rvSetting.rvDaemon)
let _listener = new Listener(TIBCO.Rendezvous.Queue.Default, _transport, rvSetting.rvSubject, null)
_listener.MessageReceived.Add(fun args->
printfn "before sent"
if messageSelector(args.Message) then
printfn "Message sent to agent: %A" args.Message
agent.Post(args.Message))
let rec dispatch() =
async{
try
TIBCO.Rendezvous.Queue.Default.Dispatch()
return! dispatch()
with
| e -> _log.Error(e.ToString())
}
Async.Start(dispatch())
with
|e -> printfn "%A" e.Message
_log.Error(e.Message)
let agent = new Agent<Message>(fun inbox ->
let rec loop() =
async{
let! (m : Message) = inbox.Receive()
// This line causes the problem
printfn "%A" (getTreasuryYieldFromMessage m)
Async.Start(treasuryAction m)
return! loop()
}
loop())
agent.Error.Add raise
[<EntryPoint>]
let main argv =
//start rvCorporate agent (fun x -> true)
agent.Start()
start rvTreasury agent treasurySelector
Console.ReadLine() |> ignore
0
I have this requirement: my F# program will do daily job at 11:00AM in the morning; I turn on my PC usually on 8:00AM, since I have been busy, so I always forget to run my F# program on time. So, I want to have a function, which can check how many seconds between now and 11:00AM, if there are 3 hours, then my program will sleep 10800 seconds, then wake up and do the job. I know I can use Windows task scheduler for this kind of job, but this way, I will not see the output from my F# program, so I have to do this in my way:
let wakeup() =
let today = DateTime.Today.ToShortDateString()
let beignTime = DateTime.Parse(today + " 11:00:00")
// Don’t know what to do yet!
For function wakeup(), I want to return an int32 number of seconds if the current time is before beginTime, if current time is later than beginTime, just return 0.
let seconds2Go = wakeup()
if (seconds2Go > 0) then
Thread.Sleep(seconds2Go * 1000)
else
printfn "Do daily job!"
But I don’t have a good idea on how to write the function wakeup().
Please offer your help.
Thanks,
John
This does the trick. You'll have to stop it with CTRL+C.
open System
open System.Threading
let (|TimeSpan|_|) value =
match TimeSpan.TryParse(value) with
| true, t -> Some t
| _ -> None
let runDaily time f =
let time = ref <| DateTime.Today.Add(time)
let rec loop() =
async {
if DateTime.Now >= !time then
time := (!time).AddDays(1.0)
f()
else do! Async.Sleep(1000)
return! loop()
}
use cts = new CancellationTokenSource()
Console.CancelKeyPress.Add(fun args -> cts.Cancel(); args.Cancel <- true)
try Async.RunSynchronously(loop(), cancellationToken = cts.Token)
with :? OperationCanceledException -> ()
[<EntryPoint>]
let main args =
match args with
| [|TimeSpan time|] ->
runDaily time (fun () ->
//TODO: program logic
)
0
| _ -> eprintfn "Usage: program.exe time"; 1
Usage
program.exe 11:00 //run every day at 11AM
let wakeup () =
let beginTime = DateTime.Today + TimeSpan(11,0,0)
beginTime.Subtract(DateTime.Now).TotalSeconds |> int
Why don't you jut use the Windows Scheuler to run the program at 11:00?
let wakeup () = 11.0*60.0*60.0 - (DateTime.Now - DateTime.Today).TotalSeconds |> int