4

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
ildjarn
  • 62,044
  • 9
  • 127
  • 211
Ramesh Kadambi
  • 546
  • 6
  • 17

3 Answers3

4

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_
  }
Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • note `IEnumerator` is in `System.Collections`, not `System.Collections.Generic` – Dax Fohl May 08 '15 at 16:32
  • Also note `Reset` and `MoveNext` should be defined in the non-generic interface instead of the generic one. – Dax Fohl May 08 '15 at 16:34
  • @Fyodor The solution you proposed gives me the following error: C:\Dev\ATF\framework\persisitence\persistence.fs(74,15): error FS0033: The type 'System.Collections.Generic.IEnumerator<_>' expects 1 type argument(s) but is given 0. However when I do implement another Current returning an obj type as you have cast it works. – Ramesh Kadambi May 08 '15 at 16:41
  • @rk540 see my first comment. – Dax Fohl May 08 '15 at 16:44
  • @rk540 It gives you this error because `IEnumerator` is in different namespace! So that when you just type `IEnumerator`, the compiler thinks you're referring to `System.Collections.Generic.IEnumerator` rather than `System.Collections.IEnumerator`. Use namespace-qualified name. – Fyodor Soikin May 08 '15 at 16:45
1

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.

Richard
  • 106,783
  • 21
  • 203
  • 265
0

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
Ramesh Kadambi
  • 546
  • 6
  • 17