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
Related
I am trying to convert a C# class into F#:
type Aggregator<'T when 'T : (new : unit -> 'T)>()=
static let ApplyMethod = "Apply"
member val private _aggregations : IDictionary<Type, obj> = new Dictionary<Type, obj>()
member val AggregateType = typeof<'T> with get
member val Alias = Unchecked.defaultof<string> with get
However it seems that even this simple code cannot compile:
Program.fs:1189 This expression was expected to have type
'IDictionary<Type,obj>'
but here has type
'Dictionary<Type,obj>'
Does it mean that a field declared with an interface IDictionary<Type, obj> type cannot infer the value passed knowing that this particular value implements that interface Dictionary<Type, obj>?
Actually if I am explicitely upcasting to IDictionary<Type, obj>:
member val private _aggregations : IDictionary<Type, obj> =
(new Dictionary<Type, obj>() :> IDictionary<Type, obj>)
This works, does it mean that F# is stricter than C# in that regard?
As pointed in the comment, F# does require an explicit:
type Aggregator<'T when 'T : (new : unit -> 'T)>()=
static let ApplyMethod = "Apply"
member val private _aggregations : IDictionary<Type, obj> = new Dictionary<Type, obj>() :> IDictionary<Type, obj>)
member val AggregateType = typeof<'T> with get
member val Alias = Unchecked.defaultof<string> with get
Side notes:
Btw, not everything what you can do in C# is possible in F# (no protected access modifier for example).
Result of the conversion:
type Aggregator<'T when 'T : (new : unit -> 'T) and 'T : not struct> (overrideMethodLookup : IEnumerable<MethodInfo>)=
let aggregations : IDictionary<Type, obj> = (new Dictionary<Type, obj>() :> IDictionary<Type, obj>)
let aggregateType = typeof<'T>
let mutable alias = Unchecked.defaultof<string>
do
alias <- typeof<'T>.Name.ToTableAlias();
overrideMethodLookup.Each(fun (method : MethodInfo) ->
let mutable step = Unchecked.defaultof<obj>
let mutable eventType = method.GetParameters().Single<ParameterInfo>().ParameterType;
if eventType.Closes(typedefof<Event<_>>) then
eventType <- eventType.GetGenericArguments().Single();
step <- typedefof<EventAggregationStep<_,_>>.CloseAndBuildAs<obj>(method, [| typeof<'T>; eventType |]);
else
step <- typedefof<AggregationStep<_,_>>.CloseAndBuildAs<obj>(method, [| typeof<'T>; eventType |]);
aggregations.Add(eventType, step)
) |> ignore
static let ApplyMethod = "Apply"
new() = new Aggregator<'T>(typeof<'T>.GetMethods()
|> Seq.where (fun x -> x.Name = ApplyMethod &&
x.GetParameters().Length = 1))
member this.Add<'TEvent>(aggregation: IAggregation<'T, 'TEvent>) =
if aggregations.ContainsKey(typeof<'TEvent>) then
aggregations.[typeof<'TEvent>] <- aggregation
else
aggregations.Add(typeof<'TEvent>, aggregation)
this
member this.Add<'TEvent>(application: Action<'T, 'TEvent>) =
this.Add(new AggregationStep<'T, 'TEvent>(application));
interface IAggregator<'T> with
member this.AggregatorFor<'TEvent>() =
if aggregations.ContainsKey(typeof<'TEvent>) then
aggregations.[typeof<'TEvent>].As<IAggregation<'T, 'TEvent>>()
else
null
member this.Build(events, session, state) =
events.Each(fun (x : IEvent) -> x.Apply(state, this)) |> ignore
state
member this.Build(events, session) =
(this :> IAggregator<'T>).Build(events, session, new 'T());
member this.EventTypes =
aggregations.Keys.ToArray();
member this.AggregateType =
aggregateType
member this.Alias =
alias
member this.AppliesTo(stream) =
stream.Events.Any(fun x -> aggregations.ContainsKey(x.Data.GetType()));
I want to derive a class from System.IO.BinaryWriter, and call it's constructor with a custom Stream, an implementation of which captures a constructor argument of the derived type. For the life of me, I cannot figure out whether even this is possible at all. What I am essentially trying to do, slightly trimmed to be an MCV, is
type HashingBinaryWriter private
(hasher : System.Security.Cryptography.HashAlgorithm,
stream : System.IO.Stream) =
inherit System.IO.BinaryWriter(stream)
let unsupp() = raise(System.NotSupportedException())
let hash_stream =
{ new System.IO.Stream() with
member __.CanRead = false
member __.CanSeek = false
member __.CanWrite = true
member __.Length = unsupp()
member __.Position with get() = unsupp() and set(_) = unsupp()
member __.Seek(_,_) = unsupp()
member __.SetLength _ = unsupp()
member __.Read(_,_,_) = unsupp()
member __.Flush() = ()
member __.Write(buffer, offset, count) =
hasher.TransformBlock(buffer, offset, count, null, 0) |> ignore
}
new(hasher) = new HashingBinaryWriter(hasher, hash_stream)
// Or, alternatively
new(hasher) as me = new HashingBinaryWriter(hasher, me.hash_stream)
The last line fails to compile because hash_stream is undefined, in either form. Apparently, as this answer suggests, the scope of constructor arguments is different from that of class declaration body, but I need to understand what is going on here (and if possible the why behind the F# design decision).
Indeed, I can see some workarounds (convert hash_stream to a private property, for example), but my vocabulary of F# idioms is lacking this one. My second question is, then, what would be the idiomatic way of doing this.
There are multiple ways to do this - and I guess the right choice depends on how your complete implementation will look.
If you wanted something that is as close to the version in your question as possible, then you can just move unsupp and hash_stream inside the constructor:
type HashingBinaryWriter private
(hasher : System.Security.Cryptography.HashAlgorithm,
stream : System.IO.Stream) =
inherit System.IO.BinaryWriter(stream)
new(hasher : System.Security.Cryptography.HashAlgorithm) =
let unsupp() = raise(System.NotSupportedException())
let hash_stream =
{ new System.IO.Stream() with
member __.CanRead = false
member __.CanSeek = false
member __.CanWrite = true
member __.Length = unsupp()
member __.Position with get() = unsupp() and set(_) = unsupp()
member __.Seek(_,_) = unsupp()
member __.SetLength _ = unsupp()
member __.Read(_,_,_) = unsupp()
member __.Flush() = ()
member __.Write(buffer, offset, count) =
hasher.TransformBlock(buffer, offset, count, null, 0) |> ignore
}
new HashingBinaryWriter(hasher, hash_stream)
I guess that this can easily get ugly if your implementation of hash_stream gets longer. In that case, it would make more sense to do what John suggests in the comments and move the implementation of the stream outside of the class, perhaps into a helper module:
let unsupp() = raise(System.NotSupportedException())
let createHashStream (hasher : System.Security.Cryptography.HashAlgorithm) =
{ new System.IO.Stream() with
member __.CanRead = false
member __.CanSeek = false
member __.CanWrite = true
member __.Length = unsupp()
member __.Position with get() = unsupp() and set(_) = unsupp()
member __.Seek(_,_) = unsupp()
member __.SetLength _ = unsupp()
member __.Read(_,_,_) = unsupp()
member __.Flush() = ()
member __.Write(buffer, offset, count) =
hasher.TransformBlock(buffer, offset, count, null, 0) |> ignore
}
type HashingBinaryWriter private
(hasher : System.Security.Cryptography.HashAlgorithm,
stream : System.IO.Stream) =
inherit System.IO.BinaryWriter(stream)
new(hasher : System.Security.Cryptography.HashAlgorithm) =
new HashingBinaryWriter(hasher, createHashStream hasher)
I'm a newbie F# programmer, and I'm having some trouble getting the syntax for my F# program correct.
Essentially, I want to turn this C# code into F#:
class MyRiskyObject : BaseObject
{
private string field;
public MyRiskyObject(object foo, string data)
: base(foo)
{
try
{
this.data = RiskyOperation(data);
}
catch (ArgumentException)
{
DoSomethingElse();
}
}
}
I, so far, have something like
type MyRiskyObject
inherit BaseObject
val field : string
new (foo:object, data:string) = {
inherit BaseObject(foo)
try
field = RiskyOperation()
????????
}
I just can't get the syntax right...
Edit:
here is the actual code I'm working on:
type RegExpObject =
inherit CommonObject
val RegExp : Regex
val Global : bool
member x.IgnoreCase:bool =
(x.RegExp.Options &&& RegexOptions.IgnoreCase) = RegexOptions.IgnoreCase
member x.MultiLine:bool =
(x.RegExp.Options &&& RegexOptions.Multiline) = RegexOptions.Multiline
new (env, pattern, options, global') =
{
inherit CommonObject(env, env.Maps.RegExp, env.Prototypes.RegExp)
// Here, I need to catch the exception, and instead call RaiseSyntaxError.
RegExp = new Regex(pattern, options ||| RegexOptions.ECMAScript ||| RegexOptions.Compiled)
Global = global'
}
then RegExp = new Regex(pattern, options ||| RegexOptions.ECMAScript ||| RegexOptions.Compiled)
new (env, pattern) =
RegExpObject(env, pattern, RegexOptions.None, false)
Why not just delegate the real work to a separate free function, so the work doesn't have to be done directly in your class' constructor?
let createRegex pattern options =
try
Regex(pattern, options ||| RegexOptions.ECMAScript ||| RegexOptions.Compiled)
with
| :? System.ArgumentException -> RaiseSynaxError ()
Then your class (as demonstrated in your edit) would be:
type RegExpObject(env, pattern, options, global') //'
inherit CommonObject(env, env.Maps.RegExp, env.Prototypes.RegExp)
let RegExp = createRegex pattern options
let Global = global' //'
member x.IgnoreCase =
(x.RegExp.Options &&& RegexOptions.IgnoreCase) = RegexOptions.IgnoreCase
member x.MultiLine =
(x.RegExp.Options &&& RegexOptions.Multiline) = RegexOptions.Multiline
new (env, pattern) = RegExpObject(env, pattern, RegexOptions.None, false)
An advantage here is that, as #Brian indicated, no use of val would be necessary, making the class definition much cleaner.
Try the following
type MyRiskyObject(foo : obj, data : string) as this =
inherit BaseObject(foo)
let mutable data = data;
do
try
data <- this.RiskyOperation data
with
| :? System.ArgumentException -> this.DoSomethingElse()
Alternate example with non-mutable let binding where RiskOperation and DoSomethingElse are not members of MyRiskObject
type MyRiskyObject(foo : obj, data : string) =
inherit BaseObject(foo)
let data =
try
OtherModule.RiskyOperation data
with
| :? System.ArgumentException ->
OtherModule.DoSomethingElse()
data
Why do Bind1 and Bind2 have different signatures?
type T() =
let bind(v, f) = v
member self.Bind1 = bind
member self.Bind2(a, b) = bind(a, b)
fsi reports them as
type T =
class
new : unit -> T
member Bind2 : a:'a * b:'b -> 'a
member Bind1 : (obj * obj -> obj)
end
This came up when I was playing with some computation expressions and couldn't figure out why I was getting an error message about Bind not being defined. Bind1-style didn't work, Bind2 did, and I couldn't figure out why.
Given the same objects, they do return the same result:
> q.Bind1(1:>obj,3:>obj);;
val it : obj = 1
> q.Bind2(1:>obj,3:>obj);;
val it : obj = 1
>
Using Microsoft F# Interactive, (c) Microsoft Corporation, All Rights Reserved
F# Version 1.9.7.4, compiling for .NET Framework Version v4.0.21006
Bind1 is a get property that returns a function while bind2 is a function. You can see the get accessor if you evaluate bind1 and bind2 from an instance.
> let t = new T();;
val t : T
> t.Bind1;;
val it : (obj * obj -> obj) = <fun:get_Bind1#3>
> t.Bind2;;
val it : ('a * 'b -> 'a) = <fun:it#10>
You wrote the shorthand of
member self.Bind1
with get() = bind
Using reflector you can see in Bind1 where obj comes from and the function object.
internal class get_Bind1#7 : FSharpFunc<Tuple<object, object>, object>
{
// Fields
public T self;
// Methods
internal get_Bind1#7(T self)
{
this.self = self;
}
public override object Invoke(Tuple<object, object> tupledArg)
{
object v = tupledArg.get_Item1();
object f = tupledArg.get_Item2();
return this.self.bind<object, object>(v, f);
}
}
Along with what kvb said you can add type annotation to the class to avoid the generic objects.
type T<'a, 'b>() =
let bind(v:'a, f:'b) = (v:'a)
member self.Bind1 = bind
member self.Bind2(a, b) = bind(a, b)
type T<'a,'b> =
class
new : unit -> T<'a,'b>
member Bind2 : a:'a * b:'b -> 'a
member Bind1 : ('a * 'b -> 'a)
end
To elaborate on Erik's answer, because it is impossible to have generic properties on .NET objects, F# has to pick non-generic types for v and f, which default to obj. You could choose other specific types and use a type annotation to give Bind1 a different (but still non-generic) signature.
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.