F# propper remove of ElapsedEventHandler handler - f#

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.

Related

Partially apply a function and dynamically call that function at runtime

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

Why is my mailBoxProcessor stuck at the receive method?

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

Augmenting Observable.Create for F#

I'd like to augment
public static IObservable<TSource> Create<TSource>(
Func<IObserver<TSource>, Action> subscribe)
{...}
For use in F# so that rather than calling with a Function or Action I can just use standard F# types i.e IObserver -> (unit -> unit).
How can I accomplish this?
Edit:
Adding full example. Not sure why obsAction does not work.
open System
open System.Reactive
open System.Reactive.Disposables
open System.Reactive.Linq
type Observable with
static member Create(subscribe) =
Observable.Create(fun observer -> Action(subscribe observer))
let obsDispose (observer:IObserver<_>) =
let timer = new System.Timers.Timer()
timer.Interval <- 1000.00
let handlerTick = new Timers.ElapsedEventHandler(fun sender args -> observer.OnNext("tick"))
let handlerElapse = new Timers.ElapsedEventHandler(fun sender args -> printfn "%A" args.SignalTime)
timer.Elapsed.AddHandler(handlerTick)
timer.Elapsed.AddHandler(handlerElapse)
timer.Start()
Disposable.Empty
let obsAction (observer:IObserver<_>) =
let timer = new System.Timers.Timer()
timer.Interval <- 1000.00
let handlerTick = new Timers.ElapsedEventHandler(fun sender args -> observer.OnNext("tick"))
let handlerElapse = new Timers.ElapsedEventHandler(fun sender args -> printfn "%A" args.SignalTime)
timer.Elapsed.AddHandler(handlerTick)
timer.Elapsed.AddHandler(handlerElapse)
timer.Start()
let action() =
timer.Elapsed.RemoveHandler(handlerTick)
timer.Elapsed.RemoveHandler(handlerElapse)
timer.Dispose()
action
let obsOtherAction (observer:IObserver<_>) =
let timer = new System.Timers.Timer()
timer.Interval <- 1000.00
let handlerTick = new Timers.ElapsedEventHandler(fun sender args -> observer.OnNext("tick"))
let handlerElapse = new Timers.ElapsedEventHandler(fun sender args -> printfn "%A" args.SignalTime)
timer.Elapsed.AddHandler(handlerTick)
timer.Elapsed.AddHandler(handlerElapse)
timer.Start()
new System.Action( fun () ->
timer.Elapsed.RemoveHandler(handlerTick)
timer.Elapsed.RemoveHandler(handlerElapse)
timer.Dispose())
let worksNeverStops = obsDispose |> Observable.Create |> Observable.subscribe(fun time -> printfn "Time: %A" time)
let actionWorks = obsOtherAction |> Observable.Create |> Observable.subscribe(fun time -> printfn "Time: %A" time)
let doesNotWork = obsAction |> Observable.Create |> Observable.subscribe(fun time -> printfn "Time: %A" time)
The problem you're facing is an FP gotcha.
In,
static member Create(subscribe) =
Observable.Create(fun observer -> Action(subscribe observer))
the type of subscribe is IObserver<_> -> unit -> unit.
Now there's a subtle difference between IObserver<_> -> unit -> unit
and IObserver<_> -> Action where Action : unit -> unit. The difference is that the former is curried, and the latter isn't.
When an observer subscribes, subscribe observer returns a method in to which () can be applied to get unit - your subscribe method will never actually be called until the last () is applied - which won't be until it un-subscribes by which point it will already be detached.
You can get over it by forcing it to not be curried:
let action() = ... | let action = (subscribe observer)
Action(action)
Further:
If you check the IL, the equivalent VB (function refs are more clearer in VB) versions for the Invoke for the FastFunc generated for
static member Create(subscribe) =
Observable.Create(fun observer -> Action(subscribe observer))
is:
Friend Function Invoke(ByVal arg As IObserver(Of a)) As Action
Return New Action(AddressOf New Observable-Create-Static#27-1(Of a)(Me.subscribe, arg).Invoke)
End Function
and for:
static member Create(subscribe) =
Observable.Create(fun observer ->
let action = subscribe observer
Action(action))
is:
Friend Function Invoke(ByVal arg As IObserver(Of a)) As Action
Return New Action(AddressOf New Observable-Create-Static#28-1(Me.subscribe.Invoke(arg)).Invoke)
End Function
AddressOf New Closure(Me.subscribe, arg).Invoke -> The subscribe function won't get called until the dispose action is called.
AddressOf New Closure(Me.subscribe.Invoke(arg)).Invoke -> The subscribe function actually gets called and the resulting action is returned as expected.
I hope it is now clear why the second case works, and not the first.

Why is my MailboxProcessor hanging?

I can't work out why the following code is hanging at the call to GetTotal. I don't seem to be able to debug inside the MailboxProcessor, so it's hard to see what's going on.
module Aggregator
open System
type Message<'T, 'TState> =
| Aggregate of 'T
| GetTotal of AsyncReplyChannel<'TState>
type Aggregator<'T, 'TState>(initialState, f) =
let myAgent = new MailboxProcessor<Message<'T, 'TState>>(fun inbox ->
let rec loop agg =
async {
let! message = inbox.Receive()
match message with
| Aggregate x -> return! loop (f agg x)
| GetTotal replyChannel ->
replyChannel.Reply(agg)
return! loop agg
}
loop initialState
)
member m.Aggregate x = myAgent.Post(Aggregate(x))
member m.GetTotal = myAgent.PostAndReply(fun replyChannel -> GetTotal(replyChannel))
let myAggregator = new Aggregator<int, int>(0, (+))
myAggregator.Aggregate(3)
myAggregator.Aggregate(4)
myAggregator.Aggregate(5)
let totalSoFar = myAggregator.GetTotal
printfn "%d" totalSoFar
Console.ReadLine() |> ignore
It seems to work fine when using an identical MailboxProcessor directly, rather than wrapping in the Aggregator class.
The problem is that you did not start the agent. You can either call Start after you create the agent:
let myAgent = (...)
do myAgent.Start()
Alternatively, you can create the agent using MailboxProcessor<'T>.Start instead of calling the constructor (I usually prefer this option, because it looks more functional):
let myAgent = MailboxProcessor<Message<'T, 'TState>>.Start(fun inbox -> (...) )
I suppose that you couldn't debug the agent, because the code inside agent wasn't actually running. I tried adding printfn "Msg: %A" message right after the call to Receive inside the agent (to print incoming messages for debugging) and I noticed that, after calling Aggregate, no messages were actually received by the agent... (It only blocked after calling GetTotal, which avaits reply)
As a side-note, I would probably turn GetTotal into a method, so you'd call GetTotal(). Properties are re-evaluated each time you access them, so your code does the same thing, but best practices don't recommend using properties that do complex work.
You forgot to start the mailbox:
open System
type Message<'T, 'TState> =
| Aggregate of 'T
| GetTotal of AsyncReplyChannel<'TState>
type Aggregator<'T, 'TState>(initialState, f) =
let myAgent = new MailboxProcessor<Message<'T, 'TState>>(fun inbox ->
let rec loop agg =
async {
let! message = inbox.Receive()
match message with
| Aggregate x -> return! loop (f agg x)
| GetTotal replyChannel ->
replyChannel.Reply(agg)
return! loop agg
}
loop initialState
)
member m.Aggregate x = myAgent.Post(Aggregate(x))
member m.GetTotal = myAgent.PostAndReply(fun replyChannel -> GetTotal(replyChannel))
member m.Start() = myAgent.Start()
let myAggregator = new Aggregator<int, int>(0, (+))
myAggregator.Start()
myAggregator.Aggregate(3)
myAggregator.Aggregate(4)
myAggregator.Aggregate(5)
let totalSoFar = myAggregator.GetTotal
printfn "%d" totalSoFar
Console.ReadLine() |> ignore

F# wake up program

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

Resources