user19344869
user19344869

Reputation: 3

type-parametrized overload in F#

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?

Upvotes: 0

Views: 74

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243041

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!)

Upvotes: 3

Related Questions