If I defined a record that implements an interface with an additional property the interface properties are not serialized by json.net. How do I get json.net to serialize those interface properties. Here is an example
open Newtonsoft.Json
type IRecord =
abstract member id : string
type ARec = {
Name : string
} with
interface IRecord with
member this.id = this.Name
let aRec = {Name = "John"}
Both JsonConvert.SerializeObject aRec and JsonConvert.SerializeObject (aRec :> IRecord) result in {"Name":"John"} but I expected {"Name":"John"; "id":"John"}
I tried adding ShouldSerializeid to the interface but that made no difference.
Related
If I have an interface:
type IData =
abstract member firstName: string
abstract member lastName: string
How do I define a record type that complies with this interface.
I tried something like below:
> type Data = { firstName: string; lastName: string } interface IData ;;
Snippet.js(43,63): error FS0366: No implementation was given for 'abstract member IData.firstName : string'. Note that all interface members must be implemented
and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.
From the official reference for Records:
Record fields differ from classes in that they are automatically exposed as properties
My first question is: If properties are "automatically exposed" then why do I need to "do something" to implement them.
Since the error message askes me to provide an implementation for the interface, I tried the following:
> type Data = { firstName: string; lastName: string; } interface IData with
- member this.firstName with get () = this.firstName
- member this.lastName with get () = this.lastName
type Data =
{firstName: string;
lastName: string;}
with
interface IData
end
So far so good, however now when I try to use this, I run into issues:
> let d: IData = { firstName = "john"; lastName = "doe" } ;;
error FS0001: This expression was expected to have type
'IData'
but here has type
'Data'
Another Attempt:
> let d = { firstName = "john"; lastName = "doe" }
- ;;
val d : Data = {firstName = "john";
lastName = "doe";}
> let d2: IData = d ;;
C:\Users\loref\Workspace\source-nly10r\Untitled-1(25,17): error FS0001: This expression was expected to have type
'IData'
but here has type
'Data'
So, my second question is that if Data implements IData then why can't I assign a value of Data type to a variable of IData type ?
As pointed by Gustavo, implicit interface implementation is being discussed by F# implementers, and is not currently available.
Wrt. my second question, explicit casting is required:
> let d2: IData = d :> IData ;;
val d2 : IData = {firstName = "john";
lastName = "doe";}
I have a WPF project and am struggling to figure out how to have two separate files:
ViewModelBase
ViewModel
When I put the classes in their own file, I receive a compile error for the viewModel because the ViewModelBase cannot be found.
Also, I am using a WPF project. As a result, I am unable to move files within Solution Explorer up and down based on file dependencies.
The code is below:
module MVVM
open System.ComponentModel
open Microsoft.FSharp.Quotations.Patterns
type ViewModelBase () =
let propertyChanged =
Event<PropertyChangedEventHandler,PropertyChangedEventArgs>()
let getPropertyName = function
| PropertyGet(_,pi,_) -> pi.Name
| _ -> invalidOp "Expecting property getter expression"
interface INotifyPropertyChanged with
[<CLIEvent>]
member this.PropertyChanged = propertyChanged.Publish
member private this.NotifyPropertyChanged propertyName =
propertyChanged.Trigger(this,PropertyChangedEventArgs(propertyName))
member this.NotifyPropertyChanged quotation =
quotation |> getPropertyName |> this.NotifyPropertyChanged
type ViewModel() =
inherit ViewModelBase()
let mutable firstName = ""
let mutable lastName = ""
member this.FirstName
with get() = firstName
and set(value) =
firstName <- value
base.NotifyPropertyChanged(<# this.FirstName #>)
member this.LastName
with get() = lastName
and set(value) =
lastName <- value
base.NotifyPropertyChanged(<# this.LastName #>)
member this.GetFullName() =
sprintf "%s %s" (this.FirstName) (this.LastName)
Any ideas on how I can compile this simple WPF project with a class per file?
Is it possible to decorate an object in F# with an interface using an object expression. E.g.:
type IFoo = abstract member foo : string
type IBar = abstract member bar : string
let a = { new IFoo with member x.foo = "foo" }
/// Looking for a variation on the below that does compile, the below doesn't
let b = { a with
interface IBar with
member x.Bar = "bar" }
You can't extend an object with an interface at run-time, but you could wrap it with another object:
let makeB (a: IFoo) =
{
new IFoo with
member x.foo = a.foo
interface IBar with
member x.bar = "bar"
}
let a = { new IFoo with member x.foo = "foo" }
let b = makeB a
I'm writing a generic class that has two constructors: the first one initializes every field, the second (parameter-less) should not initialize anything.
The only way I found to achieve this is calling the main constructor with "empty" arguments, i.e. Guid.Empty and null. Besides not looking good functional style to my untrained eyes, this means that I have to put a a' : null constraint on the second parameter, which I don't want:
type Container<'a when 'a : null>(id : Guid, content : 'a) =
let mutable _id = id
let mutable _content = content
new() = Container<'a>(Guid.Empty, null)
member this.Id
with get() = _id
and set(value) = _id <- value
member this.Content
with get() = _content
and set(value) = _content <- value
I see two ways to solve this:
use something like the default c# keyword instead of null (does such a thing exist in F#?)
use a different syntax to specify constructors and private fields (how?)
What is the best way to implement this class?
The F# analog to default is Unchecked.default<_>. It is also possible to use explicit fields which you don't initialize:
type Container<'a>() =
[<DefaultValue>]
val mutable _id : Guid
[<DefaultValue>]
val mutable _content : 'a
new (id, content) as this =
new Container<'a>() then
this._id <- id
this._content <- content
However, in general, your overall approach is somewhat unidiomatic for F#. Typically you'd use a simple record type (perhaps with a static method to create uninitialized containers, although this seems to have questionable benefit):
type 'a Container = { mutable id : Guid; mutable content : 'a } with
static member CreateEmpty() = { id = Guid.Empty; content = Unchecked.defaultof<_> }
In many situations, you could even use an immutable record type, and then use record update statements to generate new records with updated values:
type 'a Container = { id : Guid; content : 'a }
[<GeneralizableValue>]
let emptyContainer<'a> : 'a Container =
{ id = Guid.Empty;
content = Unchecked.defaultof<_> }
let someOtherContainer = { emptyContainer with content = 12 }
If the type will be used from languages other than F#, the following provides a natural interface in F#, and C#, for example.
type Container<'a>(?id : Guid, ?content : 'a) =
let orDefault value = defaultArg value Unchecked.defaultof<_>
let mutable _id = id |> orDefault
let mutable _content = content |> orDefault
new() = Container(?id = None, ?content = None)
new(id : Guid, content : 'a) = Container<_>(?id = Some id, ?content = Some content)
member this.Id
with get() = _id
and set(value) = _id <- value
member this.Content
with get() = _content
and set(value) = _content <- value
If it will only be used from F#, you can omit the following constructor overloads
new(id : Guid, content : 'a) = Container<_>(?id = Some id, ?content = Some content)
new() = Container()
because the overload accepting optional args handles both these cases equally well in F#.
Testing out NoRM https://github.com/atheken/NoRM from F# and trying to find a nice way to use it. Here is the basic C#:
class products
{
public ObjectId _id { get; set; }
public string name { get; set; }
}
using (var c = Mongo.Create("mongodb://127.0.0.1:27017/test"))
{
var col = c.GetCollection<products>();
var res = col.Find();
Console.WriteLine(res.Count().ToString());
}
This works OK but here is how I access it from F#:
type products() =
inherit System.Object()
let mutable id = new ObjectId()
let mutable _name = ""
member x._id with get() = id and set(v) = id <- v
member x.name with get() = _name and set(v) = _name <- v
Is there an easier way to create a class or type to pass to a generic method?
Here is how it is called:
use db = Mongo.Create("mongodb://127.0.0.1:27017/test")
let col = db.GetCollection<products>()
let count = col.Find() |> Seq.length
printfn "%d" count
Have you tried a record type?
type products = {
mutable _id : ObjectId
mutable name : string
}
I don't know if it works, but records are often good when you just need a class that is basically 'a set of fields'.
Just out of curiosity, you can try adding a parameter-less constructor to a record. This is definitely a hack - in fact, it is using a bug in the F# compiler - but it may work:
type Products =
{ mutable _id : ObjectId
mutable name : string }
// Horrible hack: Add member that looks like constructor
member x.``.ctor``() = ()
The member declaration adds a member with a special .NET name that is used for constructors, so .NET thinks it is a constructor. I'd be very careful about using this, but it may work in your scenario, because the member appears as a constructor via Reflection.
If this is the only way to get succinct type declaration that works with libraries like MongoDB, then it will hopefuly motivate the F# team to solve the problem in the future version of the language (e.g. I could easily imagine some special attribute that would force F# compiler to add parameterless constructor).
Here is a pretty light way to define a class close to your C# definition: it has a default constructor but uses public fields instead of getters and setters which might be a problem (I don't know).
type products =
val mutable _id: ObjectId
val mutable name: string
new() = {_id = ObjectId() ; name = ""}
or, if you can use default values for your fields (in this case, all null):
type products() =
[<DefaultValue>] val mutable _id: ObjectId
[<DefaultValue>] val mutable name: string