Let's say I have two simple classes :
type BaseClass() =
abstract member PreStart : unit -> unit
default x.PreStart() =
printfn "Base class PreStart method"
type DerivedClass() as this =
inherit BaseClass()
override this.PreStart() =
// I want to pass the body from outside
()
What I'm trying to do is to allow the scenario when one could pass an implementation body of the DerivedClass.PreStart method from outside. Of course I could pass just a function that could be called inside the PreStart method but this has some drawback related to the call of base.PreStart() method. I would have to pass an extra parameter to indicate whatever I want to execute the base method before the overriden body or after if at all.
What I would like to do is to allow the definition of the body like that :
let preStart = (fun (baseFn : unit->unit) ->
baseFn() // base class call before the overriden body
printfn "PreStart overriden"
baseFn() // base class call after the overriden body)
where baseFn() is a call to the base class method. This would be simple if we could pass around the delegate to base.PreStart call but this is not allowed by the compiler for obvious reasons.
My naive implementation with Quotations is as follows :
open FSharp.Quotations
open Microsoft.FSharp.Linq
open Microsoft.FSharp.Linq.QuotationEvaluation
open System.Linq.Expressions
type BaseClass() =
abstract member PreStart : unit -> unit
default x.PreStart() =
printfn "Base class PreStart method"
type DerivedClass(expression : Expr<(unit -> obj) -> obj>) as this =
inherit BaseClass()
// this is not optimal <## this :> BaseClass ##> doesn't work
let baseClinst = new BaseClass()
let bexpr = <## baseClinst ##>
let overrideExpr = expression
let baseTy = typeof<BaseClass>
let mthd = baseTy.GetMethod("PreStart")
override this.PreStart() =
let baseMthd = Expr.Call(bexpr, mthd, [])
let combined = <# ((%overrideExpr) (baseMthd.CompileUntyped())) #>
let l = QuotationEvaluator.EvaluateUntyped combined
()
let preStart = <#
fun (baseFn : unit->obj) ->
baseFn() // base class call before the overriden body
printfn "PreStart overriden"
baseFn() // base class call after the overriden body
#>
let d = new DerivedClass(preStart)
if prints the following output as expected:
> d.PreStart();;
Base class PreStart method
PreStart overriden
Base class PreStart method
val it : unit = ()
>
However I'm not happy with this code
In the DerivedClass I have to create an instance of the base class let baseClinst = new BaseClass() which servs as a base expression parameter to Expr.Call. Doing this doesn't work let bexpr = <## this :> BaseClass ##>. This is just calling another's instance of base class retrieved by reflection.
I had to modify the type of the function from unit -> unit to unit -> obj because of the return of the call to baseMthd.CompileUntyped()
It certainly not optimal and ugly.
My question is simple. How to improve this code to be more idiomatic F#? Or maybe there are other means than Quotations to achieve it?
You don't really need quotations for this. The obvious solution (that does not work) is to take a function and pass it base.PreStart as a parameter (so that the function can decide when to call the base class implementation):
type DerivedClass1(preStart) =
inherit BaseClass()
override this.PreStart() =
preStart base.PreStart
Now, this does not work because F# only allows you to call base.PreStart directly and it does not allow passing it as a function. However, you can define a private member that calls the base class implementation and pass this new member as a parameter to prestart:
type DerivedClass(preStart) =
inherit BaseClass()
member private this.BasePreStart() =
base.PreStart()
override this.PreStart() =
preStart this.BasePreStart
Related
I'm experimenting with integration of F# as scripting language and have an issue with FsiEvaluationSession.AddBoundVariable method. Problem is that this method creates variable of actual type of object, but I need to create variable of interface that it implements. I can't find AddBoundVariable<T>(string, T) or any other overload that would allow be do that.
// located in common assembly
type IFoo =
abstract Foo : unit -> unit
type FooImpl() =
interface IFoo with
member _.Foo () = ()
// located in host
session.AddBoundVariable ("foo", Foo())
session.EvalInteraction "foo.Foo()" // throws, `FooImpl` type doesn't have `Foo` method
session.EvalInteraction """
let foo : IFoo = foo
foo.Foo()
""" // throws, `IFoo` not found
Question is: how can I create variable of type that I want?
You have to explicitly cast the Foo instance to IFoo, so this should work:
session.EvalInteraction """
let foo = foo :> IFoo
foo.Foo()
"""
To avoid the indirection of FSI, you can try this in your compiled code by simply binding foo as normal first:
let foo = FooImpl()
let foo : IFoo = foo // ERROR: This expression was expected to have type 'IFoo' but here has type 'FooImpl'
let foo = foo :> IFoo // works fine
foo.Foo()
Say I have an interface ICache which defines two functions, Function1 and Function2 and I use an object expression to implement it, but I also want to add a helper function:
let WebCache =
{ new ICache with
member __.HelperFunction = //this doesn't work!
member __.Function1 = foo
member __.Function2 = bar
}
F# seems to not allow you to add any methods that are not part of the interface. Is there a workaround? If I want to do this, should I not be using an object expression in the first place?
You can define the helper function as an ordinary (local) function outside of the object expression:
let WebCache =
let helper n =
printfn "Helping %" n
{ new ICache with
member __.Function1 = helper 1
member __.Function2 = helper 2 }
When I look the definition of event class in f#
type Event<'T> =
class
new Event : unit -> Event<'T>
member this.Trigger : 'T -> unit
member this.Publish : IEvent<'T>
end
I can pass only one type on event class like
let nameChanged = new Event<unit>()
But I saw some sample too, that pass two variables like
let propertyChanged = Event<PropertyChangedEventHandler, PropertyChangedEventArgs>()
How could be that be possible?
And when I implement an interface like
module SapHandler
open SAP.Middleware.Connector
type Connector() =
let configurationChanged = Event<RfcDestinationManager.ConfigurationChangeHandler, RfcConfigurationEventArgs>()
interface IDestinationConfiguration with
member self.ChangeEventsSupported() =
false
[<CLIEvent>]
member self.ConfigurationChanged = configurationChanged.Publish
The compiler complain:
The type 'RfcDestinationManager.ConfigurationChangeHandler' has a non-standard delegate type
Why?
The definition of delegate type is:
It's possible because there's another class too:
type Event<'Delegate,'Args (requires delegate)> =
class
new Event : unit -> Event<'Delegate,'Args>
member this.Trigger : obj * 'Args -> unit
member this.Publish : IEvent<'Delegate,'Args>
end
You're getting that error because your delegate for the event is probably missing the first object sender argument which is standard in .net. You can use Control.DelegateEvent<'Delegate> to get around this
let propertyChanged = DelegateEvent<RfcDestinationManager.ConfigurationChangeHandler>()
I have the below code:
type IQuery =
abstract List<'T> : unit -> IList<'T>
let create (str)=
let getList () : IList<'T> = upcast List<'T>()
{ new IQuery with
member this.List<'T>() = getList<'T>()
And for the last line it gives me a warning stating that:
The method or function 'getList' should not be given explicit type argument(s) because it does not declare its type parameters explicitly
However if I remove <'T> from getList call then I get a compilation error as :
The member 'List<'T> : unit -> IList<'a>' does not have the correct type to override the corresponding abstract method. The required signature is 'List<'T> : unit -> IList<'T>'.
What can I do ?
You can declare getList with an explicit type parameter:
let getList<'T> () : IList<'T> = upcast List<'T>()
You then get an error:
Explicit type parameters may only be used on module or member bindings
If you then move the let binding to the top-level at the same scope as the type, it all works:
type IQuery =
abstract List<'T> : unit -> IList<'T>
let getList<'T> () : IList<'T> = upcast List<'T>()
let create (str) =
{ new IQuery with
member this.List<'T>() = getList<'T>()
}
If your real code has getList using values only in scope in create, like str, you'll need to add them as explicit parameters to getList.
I've got a class written in F# that I'm consuming in C#, that defines a method Render:
member this.Render template (context: IContext) =
let tokens = Lexer.tokenize template
let parser = new DefaultParser([for filter in _filters -> filter])
let resp = new StringBuilder()
for node in parser.Parse tokens None do
ignore <| resp.Append(node.render context)
resp.ToString()
The signature of this method is template:string -> (IContext -> string), which of course reads as "member Render takes a string parameter, then returns a function that takes an IContext and produces a string.
If I change the declaration from "member" to a let binding, defining it as a function local to the class definition:
let Render template (context: IContext) = ...
Then the signature becomes what you would expect it to be - string -> IContext -> string, which reads "Render takes a string, then an IContext and produces a string".
Is there a way to make a member behave like the let binding? This is causing issues consuming this member from C#, as the signature becomes Render(string, FastFunc<IContext, string>), which is not overly usable.
If you want to expose to C#, you should write it tupled style:
> type Foo =
- member this.Bar (param1, param2) = param1 + param2;;
type Foo =
class
member Bar : param1:int * param2:int -> int
end
That'll expose a normal .NET style method.