Consider my first attempt, a simple type in F# like the following:
type Test() =
inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
let mutable prop: string = null
member this.Prop
with public get() = prop
and public set value =
match value with
| _ when value = prop -> ()
| _ ->
let prop = value
this.OnPropertyChanged("Prop")
Now I test this via C# (this object is being exposed to a C# project, so apparent C# semantics are desirable):
[TestMethod]
public void TaskMaster_Test()
{
var target = new FTest();
string propName = null;
target.PropertyChanged += (s, a) => propName = a.PropertyName;
target.Prop = "newString";
Assert.AreEqual("Prop", propName);
Assert.AreEqual("newString", target.Prop);
return;
}
propName is properly assigned, my F# Setter is running, but the second assert is failing because the underlying value of prop isn't changed. This sort of makes sense to me, because if I remove mutable from the prop field, no error is generated (and one should be because I'm trying to mutate the value). I think I must be missing a fundamental concept.
What's the correct way to rebind/mutate prop in the Test class so that I can pass my unit test?
As a side-note, I would probably use if .. then instead of the match construct as it makes the code more succinct (patterh matching is especially valuable when you need to test the value agains multiple complex patterns). Also, public is the default access for member, so you can make the code a bit more succinct:
type Test() =
inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
let mutable prop : string = null
member this.Prop
with get() = prop
and set(value) =
if value <> prop then
prop <- value
this.OnPropertyChanged("Prop")
Try this:
type Test() =
inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
let mutable prop: string = null
member this.Prop
with public get() = prop
and public set value =
match value with
| _ when value = prop -> ()
| _ ->
prop <- value
this.OnPropertyChanged("Prop")
You need to make the binding mutable and then alter its value in your setter. In your initial code, you were just creating a new binding (also called prop) within your setter, so no change was visible.
In your pattern match you are actually binding a new value with
let prop = value
When you bind a value like this with the same name, it will shadow the other value for the scope of the newly declared one. I believe what you actually want to do is this:
prop <- value
Related
Given
type A() =
member val Prop: int = 0 with get, set
There are multiple ways to create an instance
let a0 = A() // Prop = 0
let a1 = A(Prop = 1)
let a2 = A()
a2.Prop <- 2
Now we want to enhance our class and allow passing the prop value in the constructor, but without losing the parameterless constructor we already have
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) = A1(Prop = prop) //Error
However this is an error
This is not a valid object construction expression. Explicit object
constructors must either call an alternate constructor or initialize
all fields of the object and specify a call to a super class
constructor.
which doesn't seem correct as the new constructor is actually calling an alternate constructor.
There are alternatives/workarounds to achieve the result, for example:
type A2() =
member val Prop: int = 0 with get, set
static member Create(prop: int) = A(Prop = prop)
let a21 = A2.Create(1)
type A3(?prop: int) as this =
do if prop.IsSome then this.Prop <- prop.Value
member val Prop: int = 0 with get, set
let a31 = A3(1)
however the A1 version seems the cleanest and there are no apparent reasons why it cannot be valid (it is very similar to the A2 static member)
Can someone explain why the A1 syntax cannot be valid?
The primary constructor of your object should generally take all the parameters required for constructing a valid object. As a general rule I'd make the secondary constructor parameterless and the primary take all the parameters you care about.
If you must have a parameterless primary constructor but still want to assign properties in secondary constructors you can use the then keyword for side-effectful construction.
type A1() =
member val Prop: int = 0 with get, set
new(prop: int) as this =
A1()
then
this.Prop <- prop
I don't know if it cannot be valid if somebody decides the compiler should handle it, but I thought you might be interested in how we normally solve this case.
type A4(prop: int) =
member val Prop = prop with get, set
new() = A4(0)
let a4a = A4() // 0
let a4b = A4(3) // 3
let a4c = A4(Prop=7) // 7
Following this, it's useful to know how to make private constructors.
type A4 private (prop: int) =
member val Prop = prop with get, set
private new () = A4(0)
This example is of course rather useless code, but it shows where to put the private keyword. As you probably already understand, you can use one or more private constructors, typically with many parameters, as helpers for public constructors that have fewer parameters.
Can someone clarify when to use typedefof<'T> vs. typeof<'T>?
Both typedefof<System.String> and typeof<System.String> return the same Type instance.
However, they return different instances and different information for System.Collections.Generic.List<_>.
Can I think of typedefof as a new and improved typeof? Should I just switch to always using typedefof? Or is it more subtle than that?
This ought to illustrate the difference. When you use typeof, the compiler infers type arguments and constructs a concrete type. In this case, the inferred type argument is System.Object:
let t1 = typeof<System.Collections.Generic.List<_>>
let t2 = typedefof<System.Collections.Generic.List<_>>
printfn "t1 is %s" t1.FullName
printfn "t2 is %s" t2.FullName
Output:
t1 is System.Collections.Generic.List`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
t2 is System.Collections.Generic.List`1
Because typeof can only return a constructed type, typedefof is necessary if you need a type object representing a generic type definition.
typeof is used when you want to get the System.Type object for a given type. typedefof is used when you want to get the System.Type that represents the type definition for a generic type. As an example that uses both, suppose you had a type called Generic<'a>, and you wanted to create a function that returned the System.Type object for the Generic of any given type.
type Generic<'a> = Value of 'a
let makeGenericOf<'a> () =
typedefof<Generic<_>>.MakeGenericType(typeof<'a>)
Here, you would use the typedefof function to get the type defintion, and typeof to get the type of 'a for constructing the generic Generic<'a> Type.
I really appreciate the answers from phoog, Aaron, and JLRishe. Here is what I have learned, based on their answers and my own experimentation.
There are two Type instances associated with generics.
There is a Type associated with a generic that has specific type parameters. For example, there is a Type associated with List<int> and a different Type associated with List<string>. This is what you get when you use typeof<>.
> typeof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.String]"
> typeof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Int32]"
There is a Type associated with the generic type definition itself. For example, there is a single Type associated with List<'T>, which is the same for List<int>, List<string>, and List<_>. This is what you get when you use typedefof<>.
> typedefof<List<string>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<int>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
> typedefof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[T]"
By the way, the Type class has an instance method to GetGenericTypeDefinition(). That means, the following two return the same instance:
> Object.ReferenceEquals(typeof<List<int>>.GetGenericTypeDefinition(), typedefof<List<int>>);;
val it : bool = true
What happens if you call typeof<List<_>>? You get back the Type definition for List<Object>, as phoog mentioned.
> typeof<List<_>>.ToString();;
val it : string = "Microsoft.FSharp.Collections.FSharpList`1[System.Object]"
This is all helpful to understand. For example, suppose I need to know if an object is a generic list (of any type).
// does not give me the answer I naively expected
> o.GetType() = typeof<List<_>>;;
val it : bool = false
// does this reference point to a List<'T>?
> o.GetType().IsGenericType && o.GetType().GetGenericTypeDefinition() = typedefof<List<_>>;;
val it : bool = true
Additionally, if you want to late-bound instantiate a generic type, you can use the MakeGenericType(...) method which Aaron mentioned.
> let myList = typedefof<List<_>>.MakeGenericType(typeof<int>);;
val myList : Type = Microsoft.FSharp.Collections.FSharpList`1[System.Int32]
How do I get Dapper to convert data to an F# type with an option property? Simple example:
type SomeType = {
Count : int option
}
connection.QueryAsync<SomeType>(...)
This throws:
System.InvalidOperationException
A parameterless default constructor or one matching signature
(System.Int32 count) is required for SomeType materialization
Using Nullable instead of option works:
type SomeType = {
Count : Nullable<int>
}
But it's not as desirable for various reasons. For one thing, I have cases where I use string option (because F# doesn't allow null assignments normally, which is great), and Nullable<string> doesn't compile. Is there a way to configure/instruct Dapper to automatically deal with option types? I'd like to avoid using custom mappings every time.
In case it matters, using with Npgsql.
I don't think there's any support for adding handlers for open generics, so you'd have to add a type handler for each type you need.
You could define a handler like this:
type OptionHandler<'T>() =
inherit SqlMapper.TypeHandler<option<'T>>()
override __.SetValue(param, value) =
let valueOrNull =
match value with
| Some x -> box x
| None -> null
param.Value <- valueOrNull
override __.Parse value =
if isNull value || value = box DBNull.Value
then None
else Some (value :?> 'T)
And register for the types you need like this:
SqlMapper.AddTypeHandler (OptionHandler<string>())
SqlMapper.AddTypeHandler (OptionHandler<int>())
why in the world does the constructor for a dictionary in F# allow duplicate keys and just overwrites silently?
let ``you just got dict`` = dict [ "hello","goodbye";"hello","world"]
This is very counter-intuitive behavior.
You could shadow the built-in dict function with a version that behaves as you want. You could return the more fitting IReadOnlyDictionary interface while you're at it.
let dict source =
let d = Dictionary<'K, 'V>(HashIdentity.Structural)
source |> Seq.iter d.Add
{
new IReadOnlyDictionary<'K, 'V> with
member x.ContainsKey(key) = d.ContainsKey(key)
member x.TryGetValue(key, value) = d.TryGetValue(key, &value)
member x.Item with get key = d.[key]
member x.Keys = d.Keys :> _
member x.Values = d.Values :> _
interface IReadOnlyCollection<KeyValuePair<'K, 'V>> with
member x.Count = d.Count
interface IEnumerable<KeyValuePair<'K, 'V>> with
member x.GetEnumerator() = d.GetEnumerator() :> _
interface System.Collections.IEnumerable with
member x.GetEnumerator() = d.GetEnumerator() :> _
}
I can't explain the reason for this design - just as I can't explain why Dictionary<TKey, TValue> doesn't take a sequence of KeyValuePairs as input.
However, if you look at the implementation of dict, you'll see that it internally adds each element using the indexer, like this:
foreach (Tuple<TKey, TValue> tuple in keyValuePairs)
{
TValue local = tuple.Item2;
TKey local2 = tuple.Item1;
d[new RuntimeHelpers.StructBox<TKey>(local2)] = local;
}
where d is the Dictionary being created. The indexer silently updates the dictionary entry, so this explains the mechanics of it.
Not an entire answer, I admit, but perhaps a piece of the puzzle.
You aren't going to get a why unless some Microsoft engineer explains it to you why they chose to do it that way. Regardless, it is what it is and works just as the documentation says it should:
https://msdn.microsoft.com/en-us/library/k7z0zy8k(v=vs.110).aspx
Remarks
You can also use the Item property to add new elements by setting the value
of a key that does not exist in the Dictionary<TKey, TValue>; for example,
myCollection[myKey] = myValue (in Visual Basic, myCollection(myKey) =
myValue). However, if the specified key already exists in the
Dictionary<TKey, TValue>, setting the Item property overwrites the old
value. In contrast, the Add method throws an exception if a value with the
specified key already exists.
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