This problem has been driving me mad. Here's the general gist:
I have two projects in a solution: the first is an F# console application and the second is a C# library with a C#+XAML class called DisplayWindow inheriting from the WPF Window. DisplayWindow has a method public void SetMessage(string s) {...} that makes the window display the text passed to it in big shiny letters that will probably also flash and spin arround and do everything else WPF is good at.
The problem is: From my F# program I need to make a function let openAWindow text = ??? so that it will open a new DisplayWindow asynchronously every time its called with the text. What is the best way to do this? Using async {} or System.Threading.Thread? Thanks for the help :)
Edit: I found this blog post http://deanchalk.com/2010/10/08/f-interacting-with-wpf-dispatcher-via-f-interactive-window/ that works but can sometimes (?) cause an ArgumentException with the error text "An entry with the same key already exists." so I have no idea whats going on there :(
I did this for our F# for Visualization library and then described the technique I used in my book Visual F# 2010 for Technical Computing.
Firstly, I wrote a lazy thunk that initializes WPF (including an STA UI thread and Application) when its evaluation is forced:
> let ui =
let mk() =
let wh = new ManualResetEvent(false)
let application = ref null
let start() =
let app = Application()
application := app
ignore(wh.Set())
app.Run() |> ignore
let thread = Thread start
thread.IsBackground <- true
thread.SetApartmentState ApartmentState.STA
thread.Start()
ignore(wh.WaitOne())
!application, thread
lazy(mk());;
val ui : Lazy<Application * Thread> = <unevaluated>
Then I wrote a spawn function that dispatches the application of a function f to an argument x such that it is run on the UI thread:
> let spawn : ('a -> 'b) -> 'a -> 'b =
fun f x ->
let app, thread = ui.Force()
let f _ =
try
let f_x = f x
fun () -> f_x
with e ->
fun () -> raise e
let t = app.Dispatcher.Invoke(DispatcherPriority.Send, System.Func<_, _>(f), null)
(t :?> unit -> 'b)();;
val spawn : ('a -> 'b) -> 'a -> 'b
Now it is just a case of invoking your openAWindow function on the UI thread with:
let openAWindow text =
DisplayWindow().SetMessage text
spawn openAWindow text
Related
Being new to F#, I'm trying to understand how to make graphic updates in a Form triggered by timer events.
My expectation was that the below simple routine should continue drawing new "random" lines every second.
Calling line() outside the timer event works seemingly without any problems, but I cannot get my head around why nothing gets displayed on the screen when the very same function is invoked via the timer event.
open System
open System.Drawing
open System.Windows.Forms
let form = new Form(Text="Simple Animation", Size=Size(400,500))
let pen = new Pen(Color.Red, 4.0f)
let random = new Random()
let line x =
let flexRight = random.Next(29,300)
form.Paint.Add (fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, flexRight))
let timer=new Timer(Interval=1000, Enabled=true)
timer.Tick.Add(fun time -> line())
form.Show()
Application.Run(form)
Any help very much appreciated, Thanks.
The major problem with you code is that on each timer tick just another brand new Paint event handler is added to your form instead of invoking a single registered OnPaint callback that would perform the drawing.
You may get rid of your line function definition and register instead a single Paint callback as
form.Paint.Add(fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, random.Next(29,300)))
Then on each timer tick Paint event may be fired, for example, by invalidating the form. This may be achieved by changing timer's callback code to
timer.Tick.Add(fun _ -> form.Invalidate())
The entire behaving as expected snippet is listed below:
#r "System.Windows.Forms"
open System
open System.Drawing
open System.Windows.Forms
let form = new Form(Text="Simple Animation", Size=Size(400,500))
let pen = new Pen(Color.Red, 4.0f)
let random = new Random()
form.Paint.Add(fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, random.Next(29,300)))
let timer=new System.Windows.Forms.Timer(Interval=1000, Enabled=true)
timer.Tick.Add(fun _ -> form.Invalidate())
form.Show()
UPDATE: as it came out the original intent was showing on the form the superposition of all subsequent drawn lines, I provide one of possible ways to accommodate such behavior with the help of GraphicsPath. Utilizing it would require the following changes to the snippet above:
before the line adding the form Paint event handler add the line creating the instance of GraphicsPath
let gp = new System.Drawing.Drawing2D.GraphicsPath()
change the Paint event handler to
form.Paint.Add(fun e -> gp.AddLine(30,30,350,random.Next(29,300))
e.Graphics.DrawPath(pen, gp))
I'm sorry for my bad english if somethig is not clear please ask me and I will explain.
My goal is make back end in OCaml for start to "play seriusly" with this language, I chose to do beck end project because I wanna make front end too in React for improve my skill with React too (I use OCaml for passion, and Ract for job I'm web developer)
I chose sqlite (with this lib: http://mmottl.github.io/sqlite3-ocaml/api/Sqlite3.html) as db for avoid db configuration
I have idea to make little wrapper for db calls(so if I chose to change db type I just need to change it), and make a function like this:
val exec_query : query -> 'a List Deferred.t = <fun>
but in lib I see this signature for exec function:
val exec : db -> ?cb:(row -> headers -> unit) -> string -> Rc.t = <fun>
The result is passed row by row to callback, but for my purpose I think I need to have some kind of object (list, array, etc.), but I have no idea how to make it from this function.
Can someone suggest how to proceed?
I guess you want val exec_query : query -> row List Deferred.t. Since Sqlite3 does not know about Async, you want to execute the call returning the list of rows in a separate system thread. The function In_thread.run : (unit -> 'a) -> 'a Deferred.t (optional args removed from signature) is the function to use for that. Thus you want to write (untested):
let exec_query db query =
let rows_of_query () =
let rows = ref [] in
let rc = Sqlite3.exec_no_headers db query
~cb:(fun r -> rows := r :: !rows) in
(* Note: you want to use result to handle errors *)
!rows in
In_thread.run rows_of_query
I've prepared the following WinForms code to be as simple as possible to help answer my question. You can see I have a start button which sets up and runs 3 different async computations in parallel which each do some work and then update labels with a result. I have 3 cancel buttons corresponding to each async computation being run in parallel. How can I wire up these cancel buttons to cancel their corresponding async computations, while allowing the others to continue running in parallel? Thanks!
open System.Windows.Forms
type MyForm() as this =
inherit Form()
let lbl1 = new Label(AutoSize=true, Text="Press Start")
let lbl2 = new Label(AutoSize=true, Text="Press Start")
let lbl3 = new Label(AutoSize=true, Text="Press Start")
let cancelBtn1 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
let cancelBtn2 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
let cancelBtn3 = new Button(AutoSize=true,Enabled=false, Text="Cancel")
let startBtn = new Button(AutoSize=true,Text="Start")
let panel = new FlowLayoutPanel(AutoSize=true, Dock=DockStyle.Fill, FlowDirection=FlowDirection.TopDown)
do
panel.Controls.AddRange [|startBtn; lbl1; cancelBtn1; lbl2; cancelBtn2; lbl3; cancelBtn3; |]
this.Controls.Add(panel)
startBtn.Click.Add <| fun _ ->
startBtn.Enabled <- false
[lbl1;lbl2;lbl3] |> List.iter (fun lbl -> lbl.Text <- "Loading...")
[cancelBtn1;cancelBtn2;cancelBtn3] |> List.iter (fun cancelBtn -> cancelBtn.Enabled <- true)
let guiContext = System.Threading.SynchronizationContext.Current
let work (timeout:int) = //work is not aware it is being run within an async computation
System.Threading.Thread.Sleep(timeout)
System.DateTime.Now.Ticks |> string
let asyncUpdate (lbl:Label) (cancelBtn:Button) timeout =
async {
let result = work timeout //"cancelling" means forcibly aborting, since work may be stuck in an infinite loop
do! Async.SwitchToContext guiContext
cancelBtn.Enabled <- false
lbl.Text <- result
}
let parallelAsyncUpdates =
[|asyncUpdate lbl1 cancelBtn1 3000; asyncUpdate lbl2 cancelBtn2 6000; asyncUpdate lbl3 cancelBtn3 9000;|]
|> Async.Parallel
|> Async.Ignore
Async.StartWithContinuations(
parallelAsyncUpdates,
(fun _ -> startBtn.Enabled <- true),
(fun _ -> ()),
(fun _ -> ()))
Cancelling threads un-cooperatively is generally a bad practice, so I wouldn't recommend doing that. See for example this article. It can be done when you're programming with Thread directly (using Thread.Abort), but none of the modern parallel/asynchronous libraries for .NET (such as TPL or F# Async) use this. If that's really what you need, then you'll have to use threads explicitly.
A better option is to change the work function so that it can be cooperatively cancelled. In F#, this really just means wrapping it inside async and using let! or do!, because this automatically inserts support for cancellation. For example:
let work (timeout:int) = async {
do! Async.Sleep(timeout)
return System.DateTime.Now.Ticks |> string }
Without using async (e.g. if the function is written in C#), you could pass around a CancellationToken value and use it to check if cancellation was requested (by calling ThrowIfCancellationRequestsd). Then you can start the three computations using Async.Start (creating a new CancellationTokenSource for each of the computations).
To do something until they all complete, I would probably create a simple agent (that triggers some event until it receives a specifies number of messages). I don't think there is any more direct way to do that (because Async.Parallel uses the same cancellation token for all workflows).
So, I guess that the point of this answer is - if work is meant to be cancelled, then it should be aware of the situation, so that it can deal with it appropriately.
As Tomas mentioned, forcibly stopping a thread is a bad idea, and designing something that doesn't realize it is a thread to be able to stop raises flags, in my mind, but, if you are doing a long calculation, if you are using some data structure, such as a 2 or 3D array, then one option would be to be able to set that to null, but, this violates many concepts of F#, since what your function is working on should be not only immutable, but if there is some array that is going to be changed, then nothing else should be changing it.
For example, if you need to stop a thread that is processing a file (I had to do this before), then, since the file couldn't be deleted, as it was open, then I was able to open it in Notepad, then just delete all the content, and save it, and the thread crashed.
So, you may want to do something like this in order to accomplish your goal, but, I would suggest that you re-evaluate your design and see if there is a better way to do this.
I'm trying to write a game using F# and Silverlight and am struggling a bit with immutability.
I want to decouple the game from the view a little, so I put it in a module and made its update function return a new instance of the world-state, thus providing immutability.
The view (AppControl) is responsible for drawing the world.
However, I see no way around making the world a ref cell in the view.
Now, I think the mutable state is local enough to not cause any problems (please correct me, if I'm wrong), I am just curious, if someone can think of a way to avoid mutable state completely?
Here's the outline of the application, I tried to reduce the problem down to the essence:
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Media
module Game =
type World = { State : int }
let init() =
{ State = 0 }
// immutable update loop
let updateWorld world =
{ State = world.State + 1 }
type AppControl() =
inherit UserControl()
let canvas = new Canvas()
let textBlock = new TextBlock()
let world = Game.init() |> ref // mutable world
let drawWorld (world : Game.World) =
textBlock.Text <- world.State.ToString()
// mutating game loop
let gameLoop world =
world := Game.updateWorld !world
drawWorld !world
()
do
canvas.Children.Add(textBlock)
base.Content <- canvas
CompositionTarget.Rendering.Add (fun _ -> gameLoop world)
type App() as this =
inherit Application()
let main = new AppControl()
do this.Startup.Add(fun _ -> this.RootVisual <- main)
The structure of your code looks fine - the mutable state is localized in the user interface (which is mutable anyway), so it is fine. You're not mutating the field from any closure, so you could use a mutable field (declared using let mutable world = ..) instead of ref cell.
To completely avoid the mutation, you can use asynchronous workflow (running on the GUI thread):
type AppControl() =
inherit UserControl()
let canvas = new Canvas()
let textBlock = new TextBlock()
let drawWorld (world : Game.World) =
textBlock.Text <- world.State.ToString()
// Asynchronous loop that waits for 'Rendering', updates
// the world & draws it and then continues waiting
let gameLoop world = async {
let! _ = Async.AwaitEvent CompositionTarget.Rendering
let newWorld = Game.updateWorld world
drawWorld newWorld
return! gameLoop newWorld }
do
canvas.Children.Add(textBlock)
base.Content <- canvas
gameLoop Game.init() |> Async.StartImmediate
The gameLoop function is asynchronous, so it doesn't block any thread. It is started using Async.StartImmediate, which means that it will run only on GUI thread (so accessing GUI elements & events from the body is safe). Inside the function, you can wait for event occurrence (using Async.AwaitEvent) and then do some action. The last line (return!) is a tail-call, so the function will continue running until the application is closed.
I'm trying to implement drag and drop in Silverlight using F# and asynchronous workflows.
I'm simply trying to drag around a rectangle on the canvas, using two loops for the the two states (waiting and dragging), an idea I got from Tomas Petricek's book "Real-world Functional Programming", but I ran into a problem:
Unlike WPF or WinForms, Silverlight's MouseEventArgs do not carry information about the button state, so I can't return from the drag-loop by checking if the left mouse button is no longer pressed. I only managed to solve this by introducing a mutable flag.
Would anyone have a solution for this, that does not involve mutable state?
Here's the relevant code part (please excuse the sloppy dragging code, which snaps the rectangle to the mouse pointer):
type MainPage() as this =
inherit UserControl()
do
Application.LoadComponent(this, new System.Uri("/SilverlightApplication1;component/Page.xaml", System.UriKind.Relative))
let layoutRoot : Canvas = downcast this.FindName("LayoutRoot")
let rectangle1 : Rectangle = downcast this.FindName("Rectangle1")
let mutable isDragged = false
do
rectangle1.MouseLeftButtonUp.Add(fun _ -> isDragged <- false)
let rec drag() = async {
let! args = layoutRoot.MouseMove |> Async.AwaitEvent
if (isDragged) then
Canvas.SetLeft(rectangle1, args.GetPosition(layoutRoot).X)
Canvas.SetTop(rectangle1, args.GetPosition(layoutRoot).Y)
return! drag()
else
return()
}
let wait() = async {
while true do
let! args = Async.AwaitEvent rectangle1.MouseLeftButtonDown
isDragged <- true
do! drag()
}
Async.StartImmediate(wait())
()
Thank you very much for your time!
The way to solve this issue is to use an overloaded AwaitEvent that allows you to wait for two events. Instead of just waiting for MouseMove, you can also wait for the MouseUp event - in the first case, you can continue moving and in the second case, you can return from the loop and stop drag&drop (this is actually discussed later in the book in section 16.4.5).
Here is the code - it actually uses AwaitObservable variant of the method (see below), which is a better choice in general, because it works with Observable.map and similar combinators (in case you wanted to use these).
let! args = Async.AwaitObservable(layoutRoot.MouseMove, layoutRoot.MouseUp)
match args with
| Choice1Of2(args) ->
// Handle the 'MouseMove' event with 'args' here
Canvas.SetLeft(rectangle1, args.GetPosition(layoutRoot).X)
Canvas.SetTop(rectangle1, args.GetPosition(layoutRoot).Y)
return! drag()
| Choice2Of2(_) ->
// Handle the 'MouseUp' event here
return()
As far as I know, the overloaded AwaitObservable method is not available in the F# libraries (yet), but you can get it from the book's web site, or you can use the following code:
// Adds 'AwaitObservable' that takes two observables and returns
// Choice<'a, 'b> containing either Choice1Of2 or Choice2Of2 depending
// on which of the observables occurred first
type Microsoft.FSharp.Control.Async with
static member AwaitObservable(ev1:IObservable<'a>, ev2:IObservable<'b>) =
Async.FromContinuations((fun (cont,econt,ccont) ->
let rec callback1 = (fun value ->
remover1.Dispose()
remover2.Dispose()
cont(Choice1Of2(value)) )
and callback2 = (fun value ->
remover1.Dispose()
remover2.Dispose()
cont(Choice2Of2(value)) )
// Attach handlers to both observables
and remover1 : IDisposable = ev1.Subscribe(callback1)
and remover2 : IDisposable = ev2.Subscribe(callback2)
() ))