I have the following piece of code where I need to call on my own class that I reference via a reference on a mutual class(hope that makes sense, if not the code sample explains it better).
Unfortunately it does work because I get the "Lookup on object of indeterminate type prior to..." error on the 'x.Connections.[0].Dest.Func' part.
Can anyone think of a way to fix this, possibly I will have to just go for quite a different approach?
type Node() =
member x.Connections = new System.Collections.Generic.List<Connection>()
member x.Connect other = x.Connections.Add(Connection(x, other))
member x.Func i : unit = x.Connections.[0].Dest.Func i
and Connection(source : Node, dest : Node) =
member val Source = source
member val Dest = dest
Ignore the fact that calling the Func method on Node serves no purpose, I've simplified this from the real code to try to just show what causes the problem.
Thanks in advance.
You can also just add a type annotation to Dest, which appears to be the critical symbol that throws the type inference algorithm into a loop:
type Node() =
member x.Connections = new System.Collections.Generic.List<Connection>()
member x.Connect other = x.Connections.Add(Connection(x, other))
member x.Func i : unit = x.Connections.[0].Dest.Func i
and Connection(source : Node, dest : Node) =
member val Source = source
member val Dest : Node = dest
Try declaring that method at the end:
type Node() =
member x.Connections = new System.Collections.Generic.List<Connection>()
member x.Connect other = x.Connections.Add(Connection(x, other))
and Connection(source : Node, dest : Node) =
member val Source = source
member val Dest = dest
type Node with
member x.Func i : unit = x.Connections.[0].Dest.Func i
or may be better swap the order of the types:
type Connection(source : Node, dest : Node) =
member val Source = source
member val Dest = dest
and Node() =
member x.Connections = new System.Collections.Generic.List<Connection>()
member x.Connect other = x.Connections.Add(Connection(x, other))
member x.Func i : unit = x.Connections.[0].Dest.Func i
Related
I wrote a wrapper around List. I expect the internal list to keep state but it doesn't. What am I doing wrong? The methods are definitely executed but the internal list is always empty.
open System
open System.Collections.Generic
open NUnit.Framework
type MyList() =
member this.List = List<char>()
member this.AddX =
printfn "AddX called"
this.List.Add('X')
member this.CountX: int =
printfn "CountX called"
this.List.Count
[<Test>]
let TestX () =
let mylist = MyList()
mylist.AddX
mylist.AddX
Assert.AreEqual(2, mylist.CountX)
Tried putting a mutable keyword in different places (no success)
The problem is that every time you call the List member of MyList, it creates a new list, so the class isn't keeping internal state the way you want. (You can verify this by adding a printfn statement to the List method.)
To fix this problem, change the List member to be a value, which is initialized only once per class instance:
type MyList() =
member val List = List<char>()
...
Alternatively, you can use a let-bound value instead:
type MyList() =
let list = List<char>()
member this.AddX = list.Add('X')
member this.CountX = list.Count
More F# questions. I have the implementation of a binary reader below. I want it to work like an enumerable sequence. The code below gives me the following error and I have as usual no clue how to resolve it. I have a c# implementation where I had to implement two different overrides for .Current property. I guess I have to do the same here but not sure how. As always, thanks a million in advance for your help.
error FS0366: No implementation was given for Collections.IEnumerator.get_Current() : obj. Note that all interface members must be implemented and listed under an appropriate interface declaration, e.g. interface ... with member ....
namespace persisitence
open System.Collections.Generic
open System
open System.IO
type BinaryPersistenceIn<'T>(fn: string, serializer: ('T * BinaryReader) -> unit) as this =
let stream_ = File.Open(fn, FileMode.Open, FileAccess.Read)
let reader_ = new BinaryReader(stream_)
[<DefaultValue>] val mutable current_ : 'T
let eof() =
stream_.Position = stream_.Length
interface IEnumerator<'T> with
member this.MoveNext() =
let mutable ret = eof()
if stream_.CanRead && ret then
serializer(this.current_, reader_)
ret
member this.Current
with get() = this.current_
member this.Dispose() =
stream_.Close()
reader_.Close()
member this.Reset() =
stream_.Seek((int64) 0., SeekOrigin.Begin) |> ignore
As #Richard pointed out, you need to implement IEnumerator.Current.
Here's code in response to your question "how to do it". This should work:
A few notes: (thanks to #DaxFohl)
IEnumerator is in different namespace (see code).
MoveNext and Reset are really members of IEnumerator, not IEnumerator<'t>, so that's where they should be implemented.
Dispose, however, is on IEnumerator<'t> (surprise! :-)
-
type BinaryPersistenceIn<'T>(fn: string, serializer: ('T * BinaryReader) -> unit) as this =
...
interface IEnumerator<'T> with
...
member this.Current
with get() = this.current_
interface System.Collections.IEnumerator with
member this.Current
with get() = this.current_ :> obj
member this.MoveNext() = ...
member this.Reset() = ...
And in conclusion, I must add this: are you really sure you want to implement IEnumerator? This is a rather low-lever thing, easy to get wrong. Why not use a sequence computation expression instead?
let binaryPersistenceSeq (fn: string) (serializer: BinaryReader -> 'T) =
seq {
use stream_ = File.Open(fn, FileMode.Open, FileAccess.Read)
use reader_ = new BinaryReader(stream_)
let eof() = stream_.Position = stream_.Length
while not eof() do
if stream_.CanRead then
yield serializer reader_
}
IEnumerator<T> extends IEnumerator and IEnumerator has a Current property of type object.
You need to also implement IEnumerator.Current separately from IEnumerator<T>.Current.
This version of the code compiles... as to does it really work.. will find out.
type BinaryPersistenceIn<'T>(fn: string, serializer: ('T * BinaryReader) -> unit) =
let stream_ = File.Open(fn, FileMode.Open, FileAccess.Read)
let reader_ = new BinaryReader(stream_)
[<DefaultValue>] val mutable current_ : 'T
let eof() =
stream_.Position = stream_.Length
interface IEnumerator<'T> with
member this.MoveNext() =
let mutable ret = eof()
if stream_.CanRead && ret then
serializer(this.current_, reader_)
ret
member this.Current
with get() = this.current_
member this.Dispose() =
stream_.Close()
reader_.Close()
member this.Reset() =
stream_.Seek((int64) 0., SeekOrigin.Begin) |> ignore
member this.Current
with get() = this.current_ :> obj
I know this is possible in C#, which produces simple and efficient code. --- Two objects of the same class can access each other's private parts.
class c1
{
private int A;
public void test(c1 c)
{
c.A = 5;
}
}
But It seems impossible in F#, is it true?
type c1()
let A = 0
member test (c: c1) = c.A
Interesting question. It seems to work with an explicit field but not with a let binding:
// Works
type c1 =
val private A : int
new(a) = { A = a }
member m.test(c : c1) = c.A
let someC1 = new c1(1)
let someMoreC1 = new c1(42);
let theAnswer = someC1.test someMoreC1
// Doesn't work
type c2() =
let mutable A = 42
// Compiler error: The field, constructor or member 'A' is not defined
member m.test(c : c2) = c.A
Yes, but in your example A is not semantically a private member of c1, it is more like a local variable of the constructor.
#afrischke gives an example of how to define c1 with an actual private member A (using val fields).
As section 8.6.1.3 of the F# spec states:
The functions and values defined by instance definitions are lexically scoped (and thus implicitly private) to the object being defined.
This is possible and it is widely used, for example, for checking memberwise equality:
type c1 =
member private this.A = 0
interface IEquatable<c1> with
member this.Equals (that: c1) = this.A = that.A
// of course, it can be done in a regular method as well
member this.Equals (that: c1) = this.A = that.A
You just use a directly in an instance method
type c1()
let A = 0
member x.test = A
For a static method this doesn't work as let bindings are slightly different - then you need a class definition like
type c1()
private member x.A = 0
static member test (A:c1) = A.A
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
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...