From seth at userprimary.net  Thu Sep 18 04:25:03 2008
From: seth at userprimary.net (Seth Falcon)
Date: Wed, 17 Sep 2008 19:25:03 -0700
Subject: [Rd] S4 coercion responsibility
In-Reply-To: <20080915170741.GX33406@ziti.local>
References: <48CE5B66.1050705@bank-banque-canada.ca>
	<20080915170741.GX33406@ziti.local>
Message-ID: <20080918022502.GB511@ziti.local>

A couple more comments...

* On 2008-09-15 at 10:07 -0700 Seth Falcon wrote:
> > The example is with RSQLite but the same thing happens with
> > RMySQL, and other DBI packages.

The use of as() within the various DBI packages should be
re-evaluated.  I suspect some of that code was among the first to make
heavy use of S4.  As S4 has evolved and become better documented and
understood, the DBI packages may not always have had a chance to keep
up.

> > > library("RSQLite")  Loading required package: DBI
> > > m <- dbDriver("SQLite")
> > > con <- dbConnect(m)
> > > setClass("SQLConPlus", contains=c("SQLiteConnection","integer"))
> > [1] "SQLConPlus"
> > > conPlus <- new("SQLConPlus", con, 1)
> > > dbListTables(con)
> > character(0)
> > > dbListTables(conPlus)

In the latest R-devel code (svn r46542), this behaves differently (and
works as you were hoping).  I get:

    library("RSQLite")
    setClass("SQLConPlus", contains=c("SQLiteConnection","integer"))
    dd = data.frame(a=1:3, b=letters[1:3])
    con = new("SQLConPlus", dbConnect(SQLite(), ":memory:"), 11L)
    dbWriteTable(con, "t1", dd)
    dbListTables(con)
    dbDisconnect(con)

I know that the methods package has been undergoing some improvements
recently, so it is not entirely surprising that behavior has changed.

I think the new behavior is desirable as it follows the rule that the
order of the superclasses listed in contains is used to break ties
when multiple methods match.  Here, there are two coerce() methods
(invoked via as()) one for SQLiteConnection and one, I believe
auto-generated, for integer.  Since SQLiteConnection comes first, it
is chosen.  Indeed, if you try the following, you get the error you
were originally seeing:

    setClass("SQLConMinus", contains=c("integer", "SQLiteConnection"))
    con2 = new("SQLConMinus", dbConnect(SQLite(), ":memory:"), 11L)
    
    > as(con, "integer")
    [1] 15395     2
    > as(con2, "integer")
    [1] 11
    > 

> Why not extend SQLiteConnection and add extra slots as you like.
> The dispatch will in this case be much easier to reason about.

This is still appropriate advice.  In general, inheritance should be
used with care, and multiple inheritance should be used with multiple
care.  Using representation() to add additional slots likely makes
more sense here.

+ seth

-- 
Seth Falcon | http://userprimary.net/user/

> sessionInfo()
R version 2.8.0 Under development (unstable) (--) 
i386-apple-darwin9.4.0 

locale:
C

attached base packages:
[1] stats     graphics  grDevices datasets  utils     methods   base     

other attached packages:
[1] RSQLite_0.7-0 DBI_0.2-4



[Rd] S4 coercion responsibility
Seth Falcon