1

Context:

I am trying to create a couple classes to encapsulate SQL commands in way that fits better with functional programming than the standard System.Data classes.

There are 3 main types of SQL commands that I care about,

  • commands that return nothing
  • commands that return 1 row
  • commands that return many rows

The implementation for how these must be executed is slightly different, but I want them to all share a common contract, which is why I am using class inheritance. I could also use an interface, but it doesn't seem like a big difference in this case.

I have implemented the shared contract as this base class:

[<AbstractClass>]
type ClosedCommand<'a>(text, kind, parameters) =
    inherit OpenCommand(text, kind, parameters)

    abstract member execute : cn:IDbConnection -> 'a

And the 3 sub-classes as:

type UnitCommand(text, kind, parameters) =
    inherit ClosedCommand<unit>(text, kind, parameters)

    override x.execute cn =
        let cmd = x.toCommandDefinition null
        SqlMapper.Execute(cn, cmd) |> ignore 

type SingleCommand<'a>(text, kind, parameters) =
    inherit ClosedCommand<'a>(text, kind, parameters)

    override x.execute cn =
        let cmd = x.toCommandDefinition null
        SqlMapper.QuerySingle<'a>(cn, cmd)

type PluralCommand<'a>(text, kind, parameters) =
    inherit ClosedCommand<seq<'a>>(text, kind, parameters)

    override x.execute cn =
        let cmd = x.toCommandDefinition null
        SqlMapper.Query<'a>(cn, cmd)

The core of the implementation is the SqlMapper class from the Dapper library.

Problem:

I am getting a compilation error on the subclass for commands returning nothing. Here is a screenshot:

screenshot

The message is:

The member execute : IDbConnection -> unit does not have the correct type to override the corresponding abstract method.

It looks to me like it does have the correct type. Even if I add type annotations, it still gives the same error.

The only difference between this case and the other two is that it is binding unit to 'a, whereas the other two leave it generic. That doesn't seem like it should matter.

Minimal example:

[<AbstractClass>]
type CommandBase<'a>(name) =
    member this.Name : string = name
    abstract member Execute : unit -> 'a

type UnitCommand(name) =
    inherit CommandBase<unit>(name)
    override this.Execute () = ()

type SingleCommand<'a>(name, x) =
    inherit CommandBase<'a>(name)
    override this.Execute () = x 

type MultipleCommand<'a>(name, xs) =
    inherit CommandBase<seq<'a>>(name)
    override this.Execute () = xs

How can I get this to compile?

JamesFaix
  • 8,050
  • 9
  • 37
  • 73

0 Answers0