I'm trying to make a very simple application in F#. I want just to make a tiny button with the click event, which will call the MessageBox.Show() method.
open System
open System.Windows.Forms
module FSI =
[<System.Runtime.InteropServices.DllImport("user32.dll")>]
extern bool ShowWindow(nativeint hWnd, int flags)
let HideConsole() =
let proc = System.Diagnostics.Process.GetCurrentProcess()
ShowWindow(proc.MainWindowHandle, 0)
FSI.HideConsole()
let form1 = new Form()
form1.Text <- "App"
let Test() = ( MessageBox.Show("Clicked.") )
let button1 = new Button( Text = "Click Me" )
button1.Click.AddHandler(fun _ _ -> Test())
form1.Controls.Add(button1)
[<STAThread>]
do
Application.Run(form1)
The errors occurred for the:
FSI.HideConsole() // warning
button1.Click.AddHandler(fun _ _ -> Test()) // compiler error
And they are similar.
For the 1-st:
Warning This expression should have type 'unit', but has type 'bool'. Use 'ignore' to discard the result of the expression, or 'let' to bind the result to a name.
For the 2-nd:
This expression was expected to have type unit but here has type DialogResult
F# functions implicitly return the result of their last expression, so currently your function Test() returns the result of MessageBox.Show(string). To create a function that returns void/unit, you can use the ignore operator.
let Test() = ignore (MessageBox.Show("Clicked."))
It's a bit cumbersome to have to do the extra parenthesis wrapping, so typically one uses the pipeforward (|>) operator, which doesn't require that:-
let Test() = MessageBox.Show("Clicked.") |> ignore
This can also be applied to your call of HideConsole. The compiler uses the warning to inform you that the return value of HideConsole is not used.
Related
Let's say I have a long-running calculation that I want to run in the background as not to block the UI thread. So I am going wrap it in an asynchronous computation.
/// Some long-running calculation...
let calculation arg = async{
do! Async.Sleep 1000
return arg }
Next I need to run the calculation in a loop where I switch to another thread to execute it and back to the UI thread to do something with its results.
/// Execute calculation repeatedly in for-loop and
/// display results on UI thread after every step
open System.Threading
let backgroundLoop uiAction = async {
let ctx = SynchronizationContext.Current
for arg in 0..100 do
do! Async.SwitchToThreadPool()
let! result = calculation arg
do! Async.SwitchToContext ctx
uiAction result }
Then this loop must be wrapped in another asynchronous computation to provide some means to cancel it from the UI.
/// Event-controlled cancellation wrapper
let cancelEvent = new Event<_>()
let cancellableWorker work = async {
use cToken = new CancellationTokenSource()
Async.StartImmediate(work, cToken.Token)
do! Async.AwaitEvent cancelEvent.Publish
cToken.Cancel() }
It seems now that I have implemented a functionality similar to that of BackgroundWorker. Testing it:
// Demo where the results are added to a ListBox.
// The background calculations can be stopped
// by a keypress when the ListBox has focus
open System.Windows.Forms
let fm = new Form()
let lb = new ListBox(Dock = DockStyle.Fill)
fm.Controls.Add lb
fm.Load.Add <| fun _ ->
backgroundLoop (lb.Items.Add >> ignore)
|> cancellableWorker
|> Async.StartImmediate
lb.KeyDown.Add <| fun _ ->
cancelEvent.Trigger()
[<System.STAThread>]
#if INTERACTIVE
fm.Show()
#else
Application.Run fm
#endif
It appears to be a bit of effort for something which I imagine as a relatively common workflow. Can we simplify it? Am I missing anything crucial?
Then this loop must be wrapped in another asynchronous computation to provide some means to cancel it from the UI.
Can we simplify it?
I think cancelEvent and cancellableWorker are unnecessary indirection in this case. You can use a CancellationTokenSource and cancel it directly from a UI event, instead of an Event<> that in turn cancels a token.
let calculation arg = async {
do! Async.Sleep 1000
return arg }
open System.Threading
let backgroundLoop uiAction = async {
let ctx = SynchronizationContext.Current
for arg in 0..100 do
do! Async.SwitchToThreadPool()
let! result = calculation arg
do! Async.SwitchToContext ctx
uiAction result }
open System.Windows.Forms
let fm = new Form()
let lb = new ListBox(Dock = DockStyle.Fill)
fm.Controls.Add lb
let cToken = new CancellationTokenSource()
fm.Load.Add <| fun _ ->
Async.StartImmediate (backgroundLoop (lb.Items.Add >> ignore), cToken.Token)
lb.KeyDown.Add <| fun _ -> cToken.Cancel()
Also (if you haven't already) take a look at Tomas Petricek's article on non-blocking user-interfaces in F#.
type AdderType() =
/// Appends to the container.
static member (+)
(cont:DockPanel,child:#UIElement) =
cont.Children.Add child |> ignore
child
When I make the class like the above and try something like this.
let dock = DockPanel()
let win = Window(Title = "Check the Window Style", Content = dock)
let menu = dock + Menu()
I get the error None of the types 'DockPanel,Menu' support the operator '+'. I was inspired to make the above by Phil Trelford's binding example which goes like this:
type DependencyPropertyValuePair(dp:DependencyProperty,value:obj) =
member this.Property = dp
member this.Value = value
static member (+)
(target:#UIElement,pair:DependencyPropertyValuePair) =
target.SetValue(pair.Property,pair.Value)
target
The above for some reason works. I have no idea why. Would it be possible to overload the + or some other operator so that I could elegantly add controls to containers?
Operators defined inside a class only work if one of the arguments is an instance of the class, but you can define your operator as a global operator:
let (++) (cont:DockPanel) (child:#UIElement) =
cont.Children.Add child |> ignore
child
The following should then work:
let dock = DockPanel()
let win = Window(Title = "Check the Window Style", Content = dock)
let menu = dock ++ Menu()
But to be honest, I don't think this kind of problem is a good place for using custom operators. Using + here is confusing, because you are not really adding two things in any sense. Your operator is not commutative, e.g. (a ++ b) <> (b ++ a).
I think a more idiomatic code would be to define a named function and use the |>:
let appendTo (cont:DockPanel) (child:#UIElement) =
cont.Children.Add child |> ignore
child
let dock = DockPanel()
let win = Window(Title = "Check the Window Style", Content = dock)
let menu = Menu() |> appendTo dock
The way to do it similarly as in the last example would be like this:
type ContainerType(con:Panel) =
member this.Children = con.Children
static member (+)
(child:#UIElement,cont:ContainerType) =
cont.Children.Add child |> ignore
child
let toCon = ContainerType
Actually I do like Tomas's solution better. And Phil Trelford's example looks so much like it is a part of .NET that it slipped my mind that one of the arguments in the static member (+) has DependencyPropertyValuePair as the type which is why the + operator could be overloaded in the first place. So that answers that.
Similar to this post I have a class with some members that need to be compiled with CLIEvent handlers:
static let customEvent add remove =
{ new IDelegateEvent<_> with
member this.AddHandler del = add del
member this.RemoveHandler del = remove del }
let evKnock = customEvent (fun v -> t.AddHandler(knockEvent,v)) (fun v -> t.RemoveHandler(knockEvent,v))
let evPreviewKnock = customEvent (fun v -> t.AddHandler(previewKnockEvent,v)) (fun v -> t.RemoveHandler(previewKnockEvent,v))
[<CLIEvent>] member t.Knock = evKnock
[<CLIEvent>] member t.PreviewKnock = evPreviewKnock
Now in other methods whenever I type t. I see this sort of note in Intellisense as the first thing:
The event 'PreviewKnock' has a non-standard type. If this event is
declared in another CLI language, you may need to access this method
using explicit add_PreviewKnock and remove_PreviewKnock for the event.
If this event is declared in F#, make the type of the event an
instantiation of either 'IDelegateEvent< _ >' or 'IEvent< _ , _ >'
Can I turn this warning off somehow? The events are of the type IDelegateEvent.
I was trying to reproduce this, but your code is not complete, so I'm not sure if this is the same behavior you are getting. However, if I modify your code slightly, I get the same error as you:
type A() =
let customEvent add remove =
{ new IDelegateEvent<_> with
member this.AddHandler del = add del
member this.RemoveHandler del = remove del }
let evKnock = customEvent ignore ignore
[<CLIEvent>] member t.Knock = evKnock
This seems to be because Knock is inferred to be of type IDelegateEvent<System.Delegate>.
The compiler wants you to specify a concrete delegate type - you can define your own or use a standard one from the .NET libraries. The following compiles fine:
type A() =
let customEvent add remove =
// Explicitly specify delegate type as System.EventHandler (for example)
{ new IDelegateEvent<System.EventHandler> with
member this.AddHandler del = add del
member this.RemoveHandler del = remove del }
let evKnock = customEvent ignore ignore
[<CLIEvent>] member t.Knock = evKnock
I'm new to F# and trying to dive in first and do a more formal introduction later. I have the following code:
type Person =
{
Id: int
Name: string
}
let GetPeople() =
//seq {
use conn = new SQLiteConnection(connectionString)
use cmd = new SQLiteCommand(sql, conn)
cmd.CommandType <- CommandType.Text
conn.Open()
use reader = cmd.ExecuteReader()
let mutable x = {Id = 1; Name = "Mary"; }
while reader.Read() do
let y = 0
// breakpoint here
x <- {
Id = unbox<int>(reader.["id"])
Name = unbox<string>(reader.["name"])
}
x
//}
let y = GetPeople()
I plan to replace the loop body with a yield statement and clean up the code. But right now I'm just trying to make sure the data access works by debugging the code and looking at the datareader. Currently I'm getting a System.InvalidCastException. When I put a breakpoint at the point indicated by the commented line above, and then type in the immediate windows reader["name"] I get a valid value from the database so I know it's connecting to the db ok. However if I try to put reader["name"] (as opposed to reader.["name"]) in the source file I get "This value is not a function and cannot be applied" message.
Why can I use reader["name"] in the immediate window but not in my fsharp code? How can I use string indexing with the reader?
Update
Following Jack P.'s advice I split out the code into separate lines and now I see where the error occurs:
let id = reader.["id"]
let id_unboxed = unbox id // <--- error on this line
id has the type object {long} according to the debugger.
Jack already answered the question regarding different syntax for indexing in F# and in the immediate window or watches, so I'll skip that.
In my experience, the most common reason for getting System.InvalidCastException when reading data from a database is that the value returned by reader.["xyz"] is actually DbNull.Value instead of an actual string or integer. Casting DbNull.Value to integer or string will fail (because it is a special value), so if you're working with nullable columns, you need to check this explicitly:
let name = reader.["name"]
let name_unboxed : string =
if name = DbNull.Value then null else unbox name
You can make the code nicer by defining the ? operator which allows you to write reader?name to perform the lookup. If you're dealing with nulls you can also use reader?name defaultValue with the following definition:
let (?) (reader:IDataReader) (name:string) (def:'R) : 'R =
let v = reader.[name]
if Object.Equals(v, DBNull.Value) then def
else unbox v
The code then becomes:
let name = reader?name null
let id = reader?id -1
This should also simplify debugging as you can step into the implementation of ? and see what is going on.
You can use reader["name"] in the immediate window because the immediate window uses C# syntax, not F# syntax.
One thing to note: since F# is much more concise than C#, there can be a lot going on within a single line. In other words, setting a breakpoint on the line may not help you narrow down the problem. In those cases, I normally "expand" the expression into multiple let-bindings on multiple lines; doing this makes it easier to step through the expression and find the cause of the problem (at which point, you can just make the change to your original one-liner).
What happens if you pull the item accesses and unbox calls out into their own let-bindings? For example:
while reader.Read() do
let y = 0
// breakpoint here
let id = reader.["id"]
let id_unboxed : int = unbox id
let name = reader.["name"]
let name_unboxed : string = unbox name
x <- { Id = id_unboxed; Name = name_unboxed; }
x
I have two functions, horizontal and vertical, for laying out controls. They work like this:
let verticalList = vertical [new TextBlock(Text = "one");
new TextBlock(Text = "two");
new TextBlock(Text = "three")]
Now verticalList is a control that displays the three textblocks vertically:
one
two
three
Here are the definitions:
let horizontal controls =
let wrap = new WrapPanel() in
List.iter (wrap.Children.Add >> ignore) controls ;
wrap
let vertical controls =
let stack = new StackPanel() in
List.iter (stack.Children.Add >> ignore) controls ;
stack
A problem occurs when I combine different types:
let foo = vertical [new TextBlock(Text = "Title"); vertical items]
This complains that the elements of the list are not of the same type. That is true, but they have a common supertype (UIElement).
I know I can use :> UIElement to upcast both items in the list, but this is an ugly solution. Can F# infer the common supertype. If not, why not?
It would be great if the nice looking
vertical [X; Y; Z]
doesn't have to become
vertical [(X :> UIElement); (Y :> UIElement); (Z :> UIElement)]
There are a few ways, including
type Animal() = class end
type Cat() =
inherit Animal()
type Dog() =
inherit Animal()
let animals1 : list<Animal> = [upcast new Cat(); upcast new Dog()]
let animals2 = ([upcast new Cat(); upcast new Dog()] : list<Animal>)
let animals3 = [(new Cat() :> Animal); upcast new Dog()]
animals1: type annotation at the var declaration, upcast each element
animals2: type annotation on the list expression, upcast each element
animals3: explicit type on first element, upcast rest
In a future version of F#, the upcast is likely to become unnecessary.
(See also http://cs.hubfs.net/forums/thread/9953.aspx , but nothing 'new' there.)
If you agree to sacrifice type safety for readability here you are the workaround:
open System
let to_list (tuple: Object) =
let rec list_after_index (n: int) =
let prop = tuple.GetType().GetMethod("get_Item"+n.ToString())
match prop with
| null -> []
| _ -> prop.Invoke(tuple, [||]) :: list_after_index(n+1)
match tuple with
| :? unit -> []
| _ when tuple.GetType().FullName.Contains(".Tuple`") -> list_after_index(1)
| _ -> [tuple]
then you can use it like that:
> to_list ();;
val it : obj list = []
> to_list (1);;
val it : obj list = [1]
> to_list([], 1, "2", 3.0);;
val it : obj list = [[]; 1; "2"; 3.0]
e.g. inside your vertical function.
I like my previous answer better, but building on that, if you have an app where you're constantly creating non-homogenous lists of animals, you can always do something like this:
let Animalize (x:Animal) = x // define a function to upcast
let animals4 = [ Animalize <| new Cat(); Animalize <| new Dog()]
// or even
let (~++) = Animalize // define a prefix operator to upcast (~ makes it prefix)
let animals5 = [ ++ new Cat(); ++ new Dog()]
The last is almost certainly an abuse of operators, unless you're in a very highly specialized domain and you constantly need some coercion like this and you're willing to sacrifice inherent readability for terseness.