After a hiatus from my Silverlight / F# application, i am starting back into it and am running into an issue I cannot seem to get my head around. I have a member variable of my usercontrol that is a list ref, on button clicks I want to add records to it - but it never updates. I am pretty sure it has to do with being a member but I havent figured it out.
Thanks in advance to those who take the time to view and reply.
the problem lines:
.
.
.
member this.brokers = ref List.empty
.
.
.
// this line doesn't seem to work
this.brokers := candidate :: (!this.brokers)
.
.
.
the class:
type Page() as this =
inherit UriUserControl("/xyz;component/page.xaml", "page")
do
this.firm.ItemsSource <- RpcXYZ.getFirms()
this.email.Background <- SolidColorBrush(Colors.Red)
this.addBtn.IsEnabled <- false
()
// instance data
member this.brokers = ref List.empty
// bound controls for add candidate
member this.FE : FrameworkElement = (this.Content :?> FrameworkElement)
member this.fname : TextBox = this.FE ? fname
member this.lname : TextBox = this.FE ? lname
member this.email : TextBox = this.FE ? email
member this.firm : RadComboBox = this.FE ? firms
member this.addBtn : RadButton = this.FE ? addBtn
member this.addCadidate_Click (sender : obj) (args : RoutedEventArgs) =
let inline findFirm (f : RpcXYZ.firm) =
f.id = Int32.Parse(this.firm.SelectedValue.ToString())
let candidate : SalesRep = {
id = -1 ;
fname = this.fname.Text ;
lname = this.lname.Text ;
email = this.email.Text ;
phone = "" ;
firm = List.find findFirm <| RpcXYZ.getFirms();
score = None ;
}
// this line is fine t is a list of 1 item after 1 click
let t = candidate :: (!this.brokers)
// this line doesn't seem to work
this.brokers := candidate :: (!this.brokers)
ChildWindow().Show() |> ignore ;
member this.email_Changed (o : obj) (arg : TextChangedEventArgs) =
let txtBox = (o :?> TextBox)
let emailRegex = Regex("(\w[-._\w]*\w#\w[-._\w]*\w\.\w{2,3})")
if emailRegex.IsMatch(txtBox.Text) = false then
txtBox.Background <- SolidColorBrush(Colors.Red)
this.addBtn.IsEnabled <- false
else
txtBox.Background <- new SolidColorBrush(Colors.White)
this.addBtn.IsEnabled <- true
This
member this.brokers = ref List.Empty
defines a property getter. Every time you touch .brokers, it re-runs the code on the right hand side. That's the issue.
The fix would be to define an instance variable, and return that:
let brokers = ref List.Empty
member this.Brokers = brokers
Then a single ref is allocated when an instance of the class is constructed, and you keep accessing that same ref object via the member property.
Brian already explained the problem. However, is there any reason why you're not using a mutable member (with getter and setter) and instead use a readonly member that returns a reference cell?
Using a get/set member would be more idiomatic solution:
let mutable brokers = List.Empty
member this.Brokers
with get() = brokers
and set(value) = brokers <- value
The declaration is a bit longer (unfortunately, there are no automatic properties in F#!), but the member will look like a standard property (from both F# and C#). You could then use it like this:
x.Brokers <- candidate :: x.Brokers
Although, you need the property only for public members that should be accessed from outside of the type. For private fields, you can just use the mutable value brokers directly...
Related
I just found such question on C# and converted some code to F# but sadly it still being returning it's name. The question is: where is my mistake? or is there alternative ways to have some enum-like structure with optional string returnin when I want to get A.B.ToString()
Here is my try:
[<TypeConverter(typedefof<EnumToStringUsingDescription>)>]
type FontVariant =
| [<Description("small-caps")>] smallCaps = 0
and
type EnumToStringUsingDescription() =
inherit TypeConverter()
override X.CanConvertFrom(context : ITypeDescriptorContext, sourceType : Type) =
sourceType.Equals(typedefof<Enum>);
override X.CanConvertTo(context : ITypeDescriptorContext, destinationType : Type) =
(destinationType.Equals(typedefof<String>));
override X.ConvertFrom(context : ITypeDescriptorContext, culture : System.Globalization.CultureInfo, value : obj) =
base.ConvertFrom(context, culture, value);
override X.ConvertTo(context : ITypeDescriptorContext, culture : System.Globalization.CultureInfo, value : obj, destinationType : Type) =
if (not <| destinationType.Equals(typedefof<String>)) then
raise <| new ArgumentException("Can only convert to string.", "destinationType");
if (not <| value.GetType().BaseType.Equals(typedefof<Enum>)) then
raise <| new ArgumentException("Can only convert an instance of enum.", "value");
let name = value.ToString();
let attrs =
value.GetType().GetField(name).GetCustomAttributes(typedefof<DescriptionAttribute>, false);
if (attrs.Length > 0) then attrs.[0]
else value
There are a couple of problems here. First, adding a TypeConverter will not influence .ToString().
Second, your conversion returns the attribute, not the description in the attribute. Here's a working function.
let getEnumDescription (value: Enum) =
let typ = value.GetType()
let name = value.ToString();
let attrs = typ.GetField(name).GetCustomAttributes(typedefof<DescriptionAttribute>, false)
if (attrs.Length > 0) then (attrs.[0] :?> DescriptionAttribute).Description :> obj
else name :> obj
That said, certain libraries/frameworks will use a Type converter if available. Might look something like this. It may be that you have to implement ConvertFrom/CanConvertFrom as well, I'm not sure.
type EnumToStringUsingDescription() =
inherit TypeConverter()
override X.CanConvertTo(context : ITypeDescriptorContext, destinationType : Type) = (destinationType.Equals(typedefof<String>))
override X.ConvertTo(context : ITypeDescriptorContext, culture : System.Globalization.CultureInfo, value : obj, destinationType : Type) =
let typ = value.GetType()
if (not <| typ.IsEnum) then raise <| new ArgumentException("Can only convert from enum.", "value");
if (not <| typ.Equals typeof<string>) then raise <| new ArgumentException("Can only convert to string.", "destinationType");
getEnumDescription (value :?> Enum)
Since, as Robert mentioned, enums cannot have members, and therefore you can't override ToString, you could do something like this, as a sort of compromise:
type FontVariant =
| ``small-caps`` = 0
Then, printf works as desired:
printfn "%A" FontVariant.``small-caps``
> small-caps
Also, John's suggestion to use a discriminated union is a good one. They look just like enums, minus the numeric values:
type FontVariant =
| SmallCaps
override this.ToString() =
match this with
| SmallCaps -> "small-caps"
Use the %O format (%A will use Reflection and print the case name).
printfn "%O" FontVariant.SmallCaps
If you need the numeric value, as enums provide, you can define a property:
member this.Value =
match this with
| SmallCaps -> 0
printfn "%d" FontVariant.SmallCaps.Value
From the MSDN documentation I understand that if Run is implemented it will be called automatically at the end of the computational expression. It says that:
builder.Run(builder.Delay(fun () -> {| cexpr |}))
will be generated for the computational expression. Run and/or Delay will be omitted if they are not defined in the workflow builder. I was expecting my ReaderBuilder to return a list of MyItem objects when Run is called automatically. So I do not understand why I'm getting a type mismatch error. The errors are generated by the return statement inside the ProcedureBuilder foo at the end of my code listing here. Could someone please explain what I'm misunderstanding about workflow builders and what I have implemented incorrectly?
I'm getting the following errors:
The type ''a list' is not compatible with the type 'ReaderBuilder'
Type constraint mismatch. The type 'a list is not compatible with type ReaderBuilder The type ''a list' is not compatible with the type 'ReaderBuilder'
open System
open System.Data
open System.Data.Common
open System.Configuration
let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)
type Direction =
| In
| Out
| Ref
| Return
type dbType =
| Int32
| String of int
type ReaderBuilder(cmd) =
let mutable items = []
member x.Foo = 2
member x.YieldFrom item =
items <- item::items
item
member x.Run item =
items
type ProcBuilder(procedureName:string) =
let name = procedureName
let mutable parameters = []
let mutable cmd:DbCommand = null
let mutable data = []
member x.Command with get() = cmd
member x.CreateCommand() =
factory.CreateCommand()
member x.AddParameter(p:string*dbType*Direction) =
parameters <- p::parameters
member x.Bind(v,f) =
f v
member x.Reader = ReaderBuilder(cmd)
member x.Return(rBuilder:ReaderBuilder) =
data
let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
builder.Command.Parameters.[prop].Value <- value
type MyItem() =
let mutable _a = 0
let mutable _b = String.Empty
let mutable _c = DateTime.Now
member x.a
with get() = _a
and set n = _a <- n
member x.b
with get() = _b
and set n = _b <- n
member x.c
with get() = _c
and set n = _c <- n
let proc name = ProcBuilder(name)
let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
builder.AddParameter(p)
builder
let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y =
let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
foo?x <- x
foo?y <- y
foo {
do! foo?x <- x
do! foo?y <- y
return foo.Reader {
let item = MyItem()
item.a <- r.GetInt32("a")
item.b <- r.GetString("b")
item.c <- r.GetDateTime("c")
yield! item
}
}
The problem in your example is that the foo.Reader { ... } block has a return type MyItem list (because this is what the Run member of the ReaderBuilder type returns). However, the Return member of ProcBuilder expects an argument of type ReaderBuilder.
The data field of ReaderBuilder will be always an empty list, so this is also suspicious. I think you probably want to change the Return of ProcBuilder to take an argument MyItem list instead.
However, I think that using custom computation builder for database access doesn't really give you much advantage. You're not creating a "non-standard computation" in some sense. Instead, you probably just want a nice syntax for calling commands & reading data. Using the dynamic operator can make this quite elegant even without computation builders - I wrote an article about this some time ago.
type SQLConn =
val mutable private connection : string option
member this.Connection
with get() : string = this.connection.Value
and set(v) = this.connection <- Some v
new (connection : string) = {connection = Some connection;}
new() = SQLConn #"Data Source=D:\Projects\AL\Service\ncFlow\dbase\dbflow.db3; Version=3;Password=432432434324"
I want to use "let x = 5+5" there or something like that, so how can I use private functions in my type (class) (record) , I know that I can use them if I do SQLConn() , but then I can't use val, I want to use both : val and let ...
thank you
As Tim explains, you can only use local let bindings with the implicit constructor syntax. I would definitely follow this approach as it makes F# code more readable.
Do you have any particular reason why you also want to use val in your code? You can still use them with the implicit constructor syntax, but they have to be mutable and initialized using mutation:
type SQLConn(connection:string) as x =
let mutable connection = connection
// Declare field using 'val' declaration (has to be mutable)
[<DefaultValue>]
val mutable a : int
// Initialize the value imperatively in constructor
do x.a <- 10
member this.Connection
with get() = connection and set(v) = connection <- v
new() = SQLConn #"Data Source=.."
As far as I can tell val is only needed to create fields that are not private (which may be required by some code-gen based tools like ASP.NET, but is otherwise not really useful).
The error message explains the problem:
error FS0963: 'let' and 'do' bindings are not permitted in class definitions unless an implicit construction sequence is used. You can use an implicit construction sequence by modifying the type declaration to include arguments, e.g. 'type X(args) = ...'.
The error message is suggesting that you declare your class as type SQLConn(connection) =. If you do this, you probably ought to remove the member this.Connection property, since you'll no longer have a mutable field.
A more likely workaround would be to declare x as val x : int, then put the x = 5 + 5; initializer inside your constructor.
What about the following?
type SQLConn(conn:string) =
// could put some other let bindings here...
// ex: 'let y = 5 + 5' or whatever
let mutable conn = conn
new() = SQLConn(#"some default string")
member __.Connection
with get () = conn and set v = conn <- v
Why is t.b evaluated on every call? And is there any way how to make it evaluate only once?
type test =
{ a: float }
member x.b =
printfn "oh no"
x.a * 2.
let t = { a = 1. }
t.b
t.b
An alternative version of Brian's answer that will evaluate b at most once, but won't evaluate it at all if B is never used
type Test(a:float) =
// constructor
let b = lazy
printfn "oh no"
a * 2.
// properties
member this.A = a
member this.B = b.Value
It's a property; you're basically calling the get_b() member.
If you want the effect to happen once with the constructor, you could use a class:
type Test(a:float) =
// constructor
let b = // compute it once, store it in a field in the class
printfn "oh no"
a * 2.
// properties
member this.A = a
member this.B = b
In response to your comments in Brian's post, you can fake copy-and-update record expressions using optional/named args. For example:
type Person(?person:Person, ?name, ?age) =
let getExplicitOrCopiedArg arg argName copy =
match arg, person with
| Some(value), _ -> value
| None, Some(p) -> copy(p)
| None, None -> nullArg argName
let name = getExplicitOrCopiedArg name "name" (fun p -> p.Name)
let age = getExplicitOrCopiedArg age "age" (fun p -> p.Age)
member x.Name = name
member x.Age = age
let bill = new Person(name = "Bill", age = 20)
let olderBill = new Person(bill, age = 25)
printfn "Name: %s, Age: %d" bill.Name bill.Age
printfn "Name: %s, Age: %d" olderBill.Name olderBill.Age
The previous responses suggest switching to a class, instead of using a record. If you want to stay with records (for their simple syntax and immutability), you can take this approach:
type test =
{ a : float
b : float }
static member initialize (t: test) =
{ t with b = t.a * 2. }
This is useful if the instance of test is created by another library (like a data provider from a web service or database). With this approach, you must remember to pass any instance of test that you receive from that API through the initialize function before using it in your code.
What is the F# equivalent of the following C# code? Specifically, I need to check if an event is being handled.
protected virtual void OnClicked(ClickEventArgs e) {
if (this.Clicked != null) //how can I perform this check in F#
this.Clicked(this, e);
}
Okay, I think I figured this thing out. Taking a cue from Don Syme's blog, specifically the section "The Implementation of the IEvent Module."
Instead of the following:
let validationFailedEvent = new Event<DataValidationEventHandler, DataValidationEventArgs>()
I had to implement IEvent myself and create a variable to hold the invocation list:
let mutable listeners: Delegate = null
let validationFailedEvent = { new IEvent<DataValidationEventHandler, DataValidationEventArgs> with
member x.AddHandler(d) =
listeners <- Delegate.Combine(listeners, d)
member x.RemoveHandler(d) =
listeners <- Delegate.Remove(listeners, d)
member x.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(x :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (x :?> IEvent<_,_>).RemoveHandler(h) } }
Then, to check if there are listeners, and, if not, raise an exception:
member private x.fireValidationFailedEvent(e:DataValidationEventArgs) =
match listeners with
| null -> failwith "No listeners"
| d -> d.DynamicInvoke([| box x; box e |])
An alternative way to implement RequiresSubscriptionEvent is to build on top of the existing Event functionality (using composition) and just add a counter that counts the number of registered handlers and add a property HasListeners (or even publish the number of listeners if you wanted...)
This makes the code a bit easier to use and hopefuly also safer, because if you don't check whether it has any listneres, it will still work as the usual F# code. And if you want to perform the check, you can...
type RequiresSubscriptionEvent<_>() =
let evt = new Event<_>()
let mutable counter = 0
let published =
{ new IEvent<_> with
member x.AddHandler(h) =
evt.Publish.AddHandler(h)
counter <- counter + 1;
member x.RemoveHandler(h) =
evt.Publish.RemoveHandler(h)
counter <- counter - 1;
member x.Subscribe(s) =
let h = new Handler<_>(fun _ -> s.OnNext)
x.AddHandler(h)
{ new System.IDisposable with
member y.Dispose() = x.RemoveHandler(h) } }
member x.Trigger(v) = evt.Trigger(v)
member x.Publish = published
member x.HasListeners = counter > 0
Sample usage:
type Demo() =
let evt = new RequiresSubscriptionEvent<_>()
[<CLIEvent>]
member x.OnSomething = evt.Publish
member x.FooThatFiresSomething() =
if evt.HasListeners then
evt.Trigger("foo!")
else
printfn "No handlers!"
Even though this isn't a part of standard F# libraries, it shows the great advantage of F# first class events. If there is some missing functionality, you can simply implement it yourself!
Typically, you don't need to do that check in F# (the event infrastructure checks for you):
type T() =
let ev = new Event<_>()
[<CLIEvent>]
member x.Event = ev.Publish
member x.OnClicked() =
ev.Trigger()
I followed kvb's suggestion and put this logic in a class. I copied Event from the F# sources and added a Handled property, which checks if the Delegate is null. I tried adding to, then removing handlers from the event to make sure it gets set back to null, and indeed it does.
type EventEx<'Delegate,'Args when 'Delegate : delegate<'Args,unit> and 'Delegate :> System.Delegate >() =
let mutable multicast : System.Delegate = null
static let argTypes =
let instanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
let mi = typeof<'Delegate>.GetMethod("Invoke",instanceBindingFlags)
mi.GetParameters() |> (fun arr -> arr.[1..]) |> Array.map (fun p -> p.ParameterType)
member x.Handled = (multicast <> null)
member x.Trigger(sender:obj,args:'Args) =
match multicast with
| null -> ()
| d ->
if argTypes.Length = 1 then
d.DynamicInvoke([| sender; box args |]) |> ignore
else
d.DynamicInvoke(Array.append [| sender |] (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(box args))) |> ignore
member x.Publish =
{ new IEvent<'Delegate,'Args> with
member x.AddHandler(d) =
multicast <- System.Delegate.Combine(multicast, d)
member x.RemoveHandler(d) =
multicast <- System.Delegate.Remove(multicast, d)
member e.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(e :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (e :?> IEvent<_,_>).RemoveHandler(h) } }
This article here http://geekswithblogs.net/Erik/archive/2008/05/22/122302.aspx says you do not need to check for null events in F#, though I don't know what his reference is.
This article http://blogs.msdn.com/dsyme/articles/FSharpCompositionalEvents.aspx by Don Symes goes into F# events in quite a bit of detail. It looks like events are not owned by the class in F#
From the above,
it is that events are now first-class
values in the F# langauge. Indeed,
events are not a separate notion at
all in the language design, rather,
events are just values of type
Microsoft.FSharp.Idioms.IEvent<_>, and
.NET events are effectively just
properties of this type.
And
One of the restrictions of C# is that
events can only exist as members
within classes. With the F# model,
new event values can be created just
as values as part of any expression.