-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement convert() and super() #181
Conversation
@lawremi points out that the main feature of |
https://github.com/RConsortium/OOP-WG/pull/181/files#diff-a68deddd00e9d282550d4445b10c54406e9a17fc9d44e0968ebd44bcc7248080R48-R62 illustrates the difference between |
Conflicts: tests/testthat/test-class-spec.R
Another possible name for |
Renamed to |
R/cast.R
Outdated
if (is_base_class(to)) { | ||
R7_data(from) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This removes both the class and the properties belonging to from
, while the documentation implies that only the class will be removed:
#' `cast()` provides an automatic fallback if `from` inherits from `to`. You
#' can override this if you need some special behavior other than simply
#' stripping class.
And the other code paths only remove the class
I'm not sure if that was intended, or if the documentation just needs to be more precise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
foo <- new_class("foo", parent = "character", properties = list(a = "integer"))
bar <- new_class("bar", parent = foo, properties = list(b = "integer"))
x <- bar()
x
#> <bar> chr(0)
#> @ a: int(0)
#> @ b: int(0)
# lost everything
cast(x, "character")
#> character(0)
# retains `b`
attributes(cast(x, foo))
#> $object_class
#> <R7_class>
#> @ name : foo
#> @ parent: <character>
#> @ properties:
#> $ a: <integer>
#>
#> $class
#> [1] "foo" "character" "R7_object"
#>
#> $a
#> integer(0)
#>
#> $b
#> integer(0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me what a cast should do here — should it strip properties or keep them? I think you see this inconsistency in the various paths. I'll standardise to make the minimal changes to the class.
tests/testthat/test-super.R
Outdated
method(bar, foo1) <- function(x) 1 | ||
method(bar, foo3) <- function(x) 3 | ||
|
||
expect_error(bar(super(foo3())), "Can't find method") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this is inconsistent
foo1 <- new_class("foo1")
foo2 <- new_class("foo2", foo1)
foo3 <- new_class("foo3", foo2)
bar <- new_generic("bar", "x")
method(bar, foo1) <- function(x) 1
method(bar, foo3) <- function(x) 3
# this doesnt work
bar(super(foo3()))
#> Error: Can't find method for generic `bar()` with dispatch classes:
#> - x: foo2
# but this does
bar(foo2())
#> [1] 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this is why I previously had mentioned dispatch = class_register(to)
in super()
might should be dispatch = class_dispatch(to)
, so match the fact that the C code of method_call_ ()
typically calls obj_dispatch()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmmm, I'm not sure. It feels weird to do super(foo3, foo2)
and then have it dispatch to foo1
. I think I'd rather eliminate the dispatch hierarchy here in favour of forcing the user to be explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we could talk about this one in the next OOP working group meeting?
To me it feels like super(foo3, foo2)
says "I return an object of class foo2", meaning that bar(foo2())
is identical to bar(super(foo3(), foo2))
in my head
I am interpreting your argument as: If the user wanted the foo1
behavior, then they should have done super(foo3, foo1)
, which also feels reasonable (and more explicit). But it still might be nice to discuss this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW this is consistent with convert()
if you do convert(x, to = foo2)
, you don't expect to get an instance of foo1
back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But you do get something back that you can call bar()
on, and that will dispatch to the foo1
method?
x <- foo3()
bar(x)
#> [1] 3
bar(convert(x, to = foo2))
#> [1] 1
bar(super(x, to = foo2))
#> Error: Can't find method for generic `bar()` with dispatch classes:
#> - x: foo2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. It's not directly analogous, but I think it's in the same spirit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, let's change this so that super(..., to = foo2)
behaves exactly like a foo2
Unfortunately I haven't been able to put the time into keeping up
with things, so maybe this is all irrelevent -- feel free to
ignore if it is.
I'm worried about the idea of using casting for delegating method
handling up the disptch chain. In other seetings where I've seen
this tried it looks OK at first but runs into trouble with
slightly more complex settings. Two simple scenarios:
1) A base class A, B a subclass of A, C a subclass of B.
f has methods f.A, f.B, and f.C with
f.C <- function(x) next_method(x) ## delegate to f.B
f.B <- function(x) next_method(x) ## delegate to f.A
So f(<a C>) should be handled by f.A.
2) g is another generic with methods g.A and g.B. The method f.A ia
defined as
f.A <- function(x) g(x)
So in f(<a B>) the call in f.A() to g() should be handled by g.B(),
not g.A().
Are these going to be handled properly with these casting approaches?
…--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa Phone: 319-335-3386
Department of Statistics and Fax: 319-335-3017
Actuarial Science
241 Schaeffer Hall email: ***@***.***
Iowa City, IA 52242 WWW: http://www.stat.uiowa.edu
|
Co-authored-by: Davis Vaughan <davis@rstudio.com>
Add test for self
@ltierney no problems; the development has been fairly hectic and I've been down a few dead ends. I'd no longer characterise library(R7)
A <- new_class("A")
B <- new_class("B", A)
C <- new_class("C", B)
f <- new_generic("f", "x")
# You (now) have to supply the class you want to dispatch
# to, so there is no longer any ambiguity:
method(f, C) <- function(x) c("f-C", f(super(x, B)))
method(f, B) <- function(x) c("f-B", f(super(x, A)))
method(f, A) <- function(x) "f-A"
f(C())
#> [1] "f-C" "f-B" "f-A"
method(f, A) <- function(x) c("f-A", g(x))
# super() only affects dispatch for the next generic, so
# future generics used by the method dispatch on the original class
g <- new_generic("g", "x")
method(g, C) <- function(x) c("g-C", g(super(x, B)))
method(g, B) <- function(x) c("g-B", g(super(x, A)))
method(g, A) <- function(x) "g-A"
f(B())
#> [1] "f-B" "f-A" "g-B" "g-A" Created on 2022-02-23 by the reprex package (v2.0.1) |
Conflicts: NEWS.md R/class.R
We should also consider if And probably worth naming the second argument as a role model: |
Thanks -- that makes sense. So from a user perspective in a call
f(super(x, B))
super() is just (an optional) part of the syntax of calling a generic,
not an actual function that could be used outside a call to a generic.
Whether it creates an ephemeral wrapper or communicates in some other
way to the dispatch process is part of the implementation, not
something user visible. If I'm understanding this correctly ...
…On Wed, 23 Feb 2022, Hadley Wickham wrote:
@ltierney no problems; the development has been fairly hectic and I've been
down a few dead ends. I'd no longer characterise super() as a casting based
— it creates a transient wrapper that only affects dispatch for the next
generic. I think this resolves your concerns:
library(R7)
A <- new_class("A")
B <- new_class("B", A)
C <- new_class("C", B)
f <- new_generic("f", "x")
# You (now) have to supply the class you want to dispatch
# to, so there is no longer any ambiguity:
method(f, C) <- function(x) c("f-C", f(super(x, B)))
method(f, B) <- function(x) c("f-B", f(super(x, A)))
method(f, A) <- function(x) "f-A"
f(C())
#> [1] "f-C" "f-B" "f-A"
method(f, A) <- function(x) c("f-A", g(x))
# super() only affects dispatch for the next generic, so
# future generics used by the method dispatch on the original class
g <- new_generic("g", "x")
method(g, C) <- function(x) c("g-C", g(super(x, B)))
method(g, B) <- function(x) c("g-B", g(super(x, A)))
method(g, A) <- function(x) "g-A"
f(B())
#> [1] "f-B" "f-A" "g-B" "g-A"
Created on 2022-02-23 by the reprex package (v2.0.1)
—
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.
You are receiving this because you werementioned.[AA55UVHV2FQQ6XVK7ARV5X3U4TUPJA5CNFSM5O7M7H7KYY3PNVWWK3TUL52HS4DF
VREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOH2B6HJY.gif] Message ID:
***@***.***>
--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa Phone: 319-335-3386
Department of Statistics and Fax: 319-335-3017
Actuarial Science
241 Schaeffer Hall email: ***@***.***
Iowa City, IA 52242 WWW: http://www.stat.uiowa.edu
|
@ltierney Right. Currently |
I like that |
R/super.R
Outdated
#' # convert() affects every generic: | ||
#' bar2(convert(obj, to = foo1)) | ||
#' # super() only affects the _next_ generic: | ||
#' bar2(super(obj, foo1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Name to
argument
Conflicts: NEWS.md R/method-introspect.R
Fixes #110. Fixes #136