when using Microsoft.SqlServer.TransactSql.ScriptDom for parsing, implementation of visitor usually looks like this:
open Microsoft.SqlServer.TransactSql.ScriptDom
let assembly _ =
let refs = ResizeArray()
refs, {
new TSqlFragmentVisitor() with
override this.Visit(s:CreateAssemblyStatement) = refs.Add(s)
}
each such visitor takes 6 LOC, while the only difference is typehint 'CreateAssemblyStatement'
so i tried this:
let visitor<'T> _ =
let refs = ResizeArray<'T>()
refs, {
new TSqlFragmentVisitor() with
override this.Visit(s:'T) = refs.Add(s)
}
let assemblyVisitor = visitor<CreateAssemblyStatement>
let executeVisitor = visitor<ExecuteStatement>
compiler complaints about non-restricted type:
error FS3213: The member 'Visit: 'T -> unit' matches multiple overloads of the same method.
Please restrict it to one of the following:
Visit: TSqlFragment -> unit
Visit: StatementList -> unit
...
adding type constraint did not help (TSqlFragment is base class):
let visitor<'T when 'T :> TSqlFragment> _ =
let refs = ResizeArray<'T>()
refs, {
new TSqlFragmentVisitor() with
override this.Visit(s:'T) = refs.Add(s)
}
so i believe this approach is just impossible, is there any other elegant way how to factor out that typehint?
Unfortunately, this is not going to be possible - and I cannot think of an elegant workaround.
The problem is that the Visit methods of TSqlFramentVisitor are not generic. This is an overloaded Visit method that has a large number of overloads (1031 according to my VS tooltip!) Each of those methods is logically a different method (they happen to have the same name, but they could as well have all different names).
This means that there is no way to compile the generic visitor function - because the compiler would need to decide, when compiling the visitor function, which of the Visit methods to override. Unfortunately, it cannot do this - because it can only decide this when it knows what the type of the method argument is (to choose one of the 1031 methods).
(Another issue is that, if someone defined a new class that inherits from TSqlFragment, this might need to override a method that does not even exist!)
Related
Let say I have a class which iherits legacy API and overrides a virtual method which is called when something happens
type MyClass() as this =
let somethingObservable: IObservable<Something> = ...
override _.OnSomething(s: Something) = ...
How can I translate each invokation of OnSomething to a notification of somethingObservable?
That's probably a simple question, but I could not find a way to do it properly (should I use not advised ISubject?). Appreciate your help.
Using a subject like this is fine, and ensures correctness in the implementation.
Here's an example using FSharp.Control.Reactive, which gets you the idiomatic way of writing this.
type MyClass() =
inherit Legacy()
let somethingObservable =
Subject.broadcast
override _.OnSomething s =
somethingObservable |> Subject.onNext s |> ignore
member _.AsObservable =
somethingObservable |> Observable.asObservable
You can also use new Subject<_> and its methods, same thing.
In some assembly if you'd prefer not to take on the System.Reactive dependency, F# also natively supports IObservable through events.
type MyClassEvt() =
inherit Legacy()
let event = new Event<_>()
override _.OnSomething s =
event.Trigger s
member _.AsObservable =
event.Publish :> IObservable<_>
Let's consider this code:
type TransactionTypes =
| TransactionType1
| TransactionType2
type Test() =
let mutable lastTransactionType1 = DateTime.MinValue
let mutable lastTransactionType2 = DateTime.MinValue
let getLastTransaction transaction =
match transaction with
| TransactionType1 -> lastTransactionType1
| TransactionType2 -> lastTransactionType2
let updateLastTransaction transaction =
match transaction with
| TransactionType1 -> lastTransactionType1 <- DateTime.UtcNow
| TransactionType2 -> lastTransactionType2 <- DateTime.UtcNow
Now (with the understanding that I'm still learning F#), I would like to clarify a couple things:
Something like:
let a = DateTime.Now
does a permanent binding, so 'a' will always be the same time on subsequent uses.
But, my understanding is that if there is a parameter, like:
let a anyParameter = DateTime.Now
will be re-evaluated every time due to the presence of the parameter. Is that correct?
In the code above, the two let statements (getLastTransaction and updateLastTransaction) are private to the type (Test)
I could also have implemented them as:
member private this.getLastTransaction = ...
member private this.updateLastTransaction = ...
Is there any reason, for private functions to prefer let vs. member private this?
"let mutable" already implies the this. so the fields are accessible by both forms.
So, what is the advantage of one form vs. the other?
When you are working with members, F# inherits a lot of things from the .NET object model. A .NET object can have a couple of different things:
Fields - those are storing a value (just like fields of a record). They can be mutable or immutable.
Methods - those can be invoked with zero or more arguments (like functions)
Properties - those have no arguments (like fields); they can be read or written, but when this happens, some code is invoked. A property is basically a pair of getter and setter methods.
In F#, some of this is less visible. However, let corresponds to a field and member with arguments corresponds to a method. Your tricky case is a member without arguments. For example:
type A() =
member x.Foo = printfn "Hi"; 42
Will Hi be printed only once, or will it be printed each time you access Foo? To answer, it's useful to know that Foo is a property with a getter. The above is actually a syntactic sugar for the full version:
type A() =
member x.Foo
with get() = printfn "Hi"; 42
Now you can see that there is a method behind the Foo property! Each time you access Foo, the compiler will generate a call to the get() method, so Hi will be printed repeatedly.
In addition to Tomas' answer:
let mutable lastTransactionType1 = DateTime.MinValue
is equivalent in C# to:
internal DateTime lastTransactionType1 = DateTime.MinValue;
and
member private this.getLastTransaction ...
is the same IL as far as IL is concerned with
let getLastTransaction ...
In equivalent C#, both are
internal DateTime getLastTransactionMember(TransactionTypes transaction)
{
if (transaction.Tag != 1)
{
return lastTransactionType1;
}
return lastTransactionType2;
}
But for using F# in an idiomatic way, you would want to go with let.
There's also a difference in that member does let you use the methods in bindings before their declaration, which might be useful in some cases (read: hacks)
let getType1 = this.getLastTransactionMember TransactionType1 //this compiles
member private this.getLastTransactionMember transaction =
match transaction with
| TransactionType1 -> lastTransactionType1
| TransactionType2 -> lastTransactionType2
I'm trying to createa a base test class where I can setup common methods.
type BaseTest() =
member self.Assert (functionToEvaluate:bool, ?errorMessage:string) =
let a = fun () -> defaultArg errorMessage ""
match errorMessage with
| None -> NUnit.Framework.Assert.That(functionToEvaluate)
| _ -> NUnit.Framework.Assert.That(functionToEvaluate, a )
[<TestFixture>]
type MyTest () =
inherit BaseTest()
[<Test>]
member self.``test something``() =
let x = 1
self.Assert_( (x = 2))
// or
self.Assert_( (x = 2), "x value is not 2")
How to make the code "clean" (let a ... is horrible for me)
How to avoid using this/self in the derived class?
How can I write it like self.Assert(x=1) or even better just Assert(x=1) instead of self.Assert((x=1)) ?
What I want tot do (and I can do with C#) is this:
// in base test class
protected void Assert(bool test) => NUnit.Framework.Assert.That(test);
protected void Assert(bool test, string errorMessage) => NUnit.Framework.Assert.That(test, errorMessage);
// in test class
public void TestSomething() {
var x = 1
Assert(x==2)
// or
Assert(x==2, "x is not 2")
}
Your problem is that you're trying to translate the C# program into F# verbatim and expect it to look "nice" without realizing that the initial C# program is already full of C#-specific tricks that exist in order to make it look "nice". One example is the base class. Why is there a base class? Does it represent something? No, it doesn't: it's there for the sole purpose of avoiding a class name when calling these functions - i.e. you write Assert instead of SomeHelper.Assert. This stems out of the fact that in C# you can't have free-standing functions.
But in F# - you can!
let assrt x = NUnit.Framework.Assert.That(x)
let assrtWith x msg = NUnit.Framework.Assert.That(x, msg)
[<TestFixture>]
type SaleRepositoryTest () =
[<Test>]
member self.``test something``() =
let x = 1
assrt (x=2)
assrtWith (x=2) "x is not 2"
(Note that you can't use the name assert, because it's a keyword)
Also note that you generally don't need classes. The fact that in C# you can't do anything without them is a giant mistake that grows out of Java design, which was a misunderstanding of OO.
You can have free-standing functions. If I remember correctly, NUnit should be able to discover such tests just fine (though I can't verify right now):
let [<Test>] ``test something``() =
let x = 1
assrt (x=2)
assrtWith (x=2) "x is not 2"
Finally, I strongly recommend that you consider FsUnit. It can bind to NUnit (if you're locked into that) and provides a nice library of F#-idiomatic assertions.
In my first attempt to create a type provider, I have a ProvidedTypeDefinition for a message:
// Message type
let mTy = ProvidedTypeDefinition(asm, ns, message.Key, Some(typeof<ValueType>),
HideObjectMethods = true, IsErased = false)
// Direct buffer
let bufferField = ProvidedField("_directBuffer", typeof<IDirectBuffer>)
mTy.AddMember bufferField
let mCtor1 =
ProvidedConstructor(
[ProvidedParameter("buffer", typeof<IDirectBuffer>)],
InvokeCode = fun args ->
match args with
| [this;buffer] ->
Expr.FieldSet (this, bufferField, <## %%buffer:IDirectBuffer ##>)
| _ -> failwith "wrong ctor params"
)
mTy.AddMember mCtor1
Then I need to create an instance of that type in a method of another provided type. I am doing this:
let mMethod = ProvidedMethod(message.Key, [ProvidedParameter("buffer", typeof<IDirectBuffer>)], mTy)
mMethod.InvokeCode <- (fun [this;buffer] ->
let c = mTy.GetConstructors().Last()
Expr.NewObject(c, [ buffer ])
)
ILSpy shows the following C# code equivalent for the method:
public Car Car(IDirectBuffer buffer)
{
return new Car(buffer);
}
and it also shows that the Car struct is present in the test assembly (this test assembly builds OK unless I access the Car method):
But when I try to create the Car via the method like this:
type CarSchema = SbeProvider<"Path\to\SBETypeProvider\SBETypeProvider\Car.xml">
module Test =
let carSchema = CarSchema()
let car = carSchema.Car(null)
I get the following errors:
The module/namespace 'SBETypeProvider' from compilation unit 'tmp5CDE' did not contain the namespace, module or type 'Car'
A reference to the type 'SBETypeProvider.Car' in assembly 'tmp5CDE' was found, but the type could not be found in that assembly
What I am doing wrong? The picture shows that the type is here. Why I cannot create it?
I looked through many type providers on GitHub and cannot find a clear example how to generate a ProvidedTypeDefinition from another one.
This might not be the problem, but at a quick glance it looks like the line you linked might actually be the issue:
let mTy = ProvidedTypeDefinition(asm, ns, message.Key, Some(typeof<ValueType>),
HideObjectMethods = true, IsErased = false)
This type is being added to the ty provided type (the one that will actually be written to the temporary assembly) and so shouldn't have the assembly and namespace specified itself.
let mTy = ProvidedTypeDefinition(message.Key, Some(typeof<ValueType>),
HideObjectMethods = true, IsErased = false)
Might work better. Generated types are a bit of a black art though, with very little documentation, so it's possible (probable?) that there will be other issues you might find.
On a more general note, for creating provided types what I normally end up doing is returning the provided constructor as a value which can then be embedded in the invoke code for other properties/functions using Expr.Call. This is especially important for erased types, as reflection will not work on them anyway.
I hit something new to me with the following piece of code when following the equivalent in C# here. The compiler gives multiple errors basically telling the IConnectableObservable created in source.Publish() does not match IObservable even though it derives from it (according to the MSDN article linked).
Is there something in F# that is different with regard to C# concerning inheritance in this case or can someone provider pointers as to what is going on? Have I just made a typo I can't see? What comes to the heading regarding covariance, it's just a wild guess as I'm at least temporarily out of ideas. And so, maybe writing somewhere may help me and others...
One example of the many error messages:
No overloads match for method 'Create'. The available overloads are shown below (or in the Error List window).
No overloads match for method 'Switch'. The available overloads are shown below (or in the Error List window).
Error Possible overload: '(extension) IObservable.Switch<'TSource>() :
IObservable<'TSource>'. Type constraint mismatch. The type
IObservable<IConnectableObservable<'b>> is not compatible with type
IObservable<IObservable<'a>> The type 'IObservable<'a>' does not match the type 'IConnectableObservable<'b>'.
open System.Reactive.Concurrency
open System.Reactive.Disposables
open System.Reactive.Subjects
open System.Reactive.Linq
type Observable with
static member inline Suspendable(source: IObservable<_>, suspend: IObservable<bool>, isSuspendedInitially: bool): IObservable<_> =
Observable.Create<_>(fun observer ->
let shared = source.Publish()
let pausable =
suspend.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(fun p -> if p then shared else Observable.Empty<_>())
.Switch()
new CompositeDisposable(pausable.Subscribe(observer), shared.Connect()))
The corresponding C# code
public static class RxExtensions
{
public static IObservable<T> Suspendable<T>(this IObservable<T> stream, IObservable<bool> suspend, bool isSuspendedInitially)
{
return Observable.Create<T>(o =>
{
var shared = stream.Publish();
var pausable = suspend
.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(p => p ? shared : Observable.Empty<T>())
.Switch();
return new CompositeDisposable(pausable.Subscribe(o), shared.Connect());
});
}
}
This was a bit tricky, but you need to add two upcasts: shared to IObservable<_>, and the result of the lambda function to IDisposable. These would be implicit in C#, but need to be explicit in F#:
type Observable with
static member inline Suspendable (source: IObservable<_>,
suspend: IObservable<bool>,
isSuspendedInitially: bool): IObservable<'a> =
Observable.Create<_>(fun observer ->
let shared = source.Publish()
let pausable =
suspend.StartWith(isSuspendedInitially)
.TakeUntil(shared.LastOrDefaultAsync())
.DistinctUntilChanged()
.Select(fun p -> if p then shared :> IObservable<_>
else Observable.Empty<_>())
.Switch()
new CompositeDisposable(pausable.Subscribe(observer),
shared.Connect()) :> IDisposable)