* GOOPS constructors @ 2014-07-22 12:18 Marko Rauhamaa 2014-07-22 12:22 ` Tobias Brandt 0 siblings, 1 reply; 8+ messages in thread From: Marko Rauhamaa @ 2014-07-22 12:18 UTC (permalink / raw) To: guile-user Consider this simple program: ======================================================================== (use-modules (oop goops) (ice-9 optargs)) (define-class <rectangle> () (width #:accessor width #:init-keyword #:width) (height #:accessor height #:init-keyword #:height)) (define-method (area (@ <rectangle>)) (* (height @) (width @))) (define-class <square> (<rectangle>) (side #:accessor side #:init-keyword #:side)) (define-method (initialize (@ <square>) args) (let-keywords args #f ((side #f)) (next-method @ (list #:width side #:height side)))) (format #t "~S\n" (area (make <square> #:side 3))) (format #t "~S\n" (side (make <square> #:side 3))) ======================================================================== The program outputs: ======================================================================== 9 ERROR: Unbound slot in object #<<square> b76cfec0> ======================================================================== I understand that by overriding <square>'s initialize method I'm losing the magic of the default initializer. How could I have the cake and eat it, too? Marko ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: GOOPS constructors 2014-07-22 12:18 GOOPS constructors Marko Rauhamaa @ 2014-07-22 12:22 ` Tobias Brandt 2014-07-22 13:03 ` Marko Rauhamaa 0 siblings, 1 reply; 8+ messages in thread From: Tobias Brandt @ 2014-07-22 12:22 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: guile-user@gnu.org [-- Attachment #1: Type: text/plain, Size: 1500 bytes --] Couldn't just define <square> like this: (define-class <square> (<rectangle>)) then define a side method: (define-method (side (@ <square>)) (slot-ref @ 'height)) This way, you're not storing the same information twice. On 22 July 2014 14:18, Marko Rauhamaa <marko@pacujo.net> wrote: > > Consider this simple program: > > ======================================================================== > (use-modules > (oop goops) > (ice-9 optargs)) > > (define-class <rectangle> () > (width #:accessor width #:init-keyword #:width) > (height #:accessor height #:init-keyword #:height)) > > (define-method (area (@ <rectangle>)) > (* (height @) (width @))) > > (define-class <square> (<rectangle>) > (side #:accessor side #:init-keyword #:side)) > > (define-method (initialize (@ <square>) args) > (let-keywords > args #f ((side #f)) > (next-method @ (list #:width side #:height side)))) > > (format #t "~S\n" (area (make <square> #:side 3))) > (format #t "~S\n" (side (make <square> #:side 3))) > ======================================================================== > > The program outputs: > > ======================================================================== > 9 > ERROR: Unbound slot in object #<<square> b76cfec0> > ======================================================================== > > I understand that by overriding <square>'s initialize method I'm losing > the magic of the default initializer. How could I have the cake and eat > it, too? > > > Marko > > [-- Attachment #2: Type: text/html, Size: 2185 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: GOOPS constructors 2014-07-22 12:22 ` Tobias Brandt @ 2014-07-22 13:03 ` Marko Rauhamaa 2014-07-22 13:09 ` Tobias Brandt 2014-07-23 15:02 ` Barry Fishman 0 siblings, 2 replies; 8+ messages in thread From: Marko Rauhamaa @ 2014-07-22 13:03 UTC (permalink / raw) To: Tobias Brandt; +Cc: guile-user@gnu.org Tobias Brandt <tob.brandt@googlemail.com>: > Couldn't just define <square> like this: > > (define-class <square> (<rectangle>)) > > then define a side method: > > (define-method (side (@ <square>)) (slot-ref @ 'height)) > > This way, you're not storing the same information twice. Well, I was trying to find a simple toy example to demonstrate the need, but maybe I'll need to find a more compelling one. How about: ======================================================================== (define-class <error> () (msg #:getter msg #:init-keyword #:msg)) (define-class <file-error> (<error>) (path #:getter path #:init-keyword #:path) (code #:getter code #:init-keyword #:code) (fs #:getter fs #:init-keyword #:fs)) (define-method (initialize (@ <file-error>) args) (let-keywords args #f ((path #f) (code #f) (fs #f)) (next-method @ (list #:msg (format #f "File error in ~S (~S)" path code))))) ======================================================================== How do I now write the getters for path, code and fs? Marko ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: GOOPS constructors 2014-07-22 13:03 ` Marko Rauhamaa @ 2014-07-22 13:09 ` Tobias Brandt 2014-07-22 13:15 ` Marko Rauhamaa 2014-07-23 15:02 ` Barry Fishman 1 sibling, 1 reply; 8+ messages in thread From: Tobias Brandt @ 2014-07-22 13:09 UTC (permalink / raw) To: Marko Rauhamaa; +Cc: guile-user@gnu.org [-- Attachment #1: Type: text/plain, Size: 1590 bytes --] I don't have access to guile currently, so I can't try this. But I think you can just pass path, code, and fs to next-method. They should eventually end up in the default initialize. (define-method (initialize (@ <file-error>) args) (let-keywords args #f ((path #f) (code #f) (fs #f)) (next-method @ (list #:msg (format #f "File error in ~S (~S)" path code) #:path path #:code code #:fs fs)))) On 22 July 2014 15:03, Marko Rauhamaa <marko@pacujo.net> wrote: > Tobias Brandt <tob.brandt@googlemail.com>: > > > Couldn't just define <square> like this: > > > > (define-class <square> (<rectangle>)) > > > > then define a side method: > > > > (define-method (side (@ <square>)) (slot-ref @ 'height)) > > > > This way, you're not storing the same information twice. > > Well, I was trying to find a simple toy example to demonstrate the need, > but maybe I'll need to find a more compelling one. > > How about: > > ======================================================================== > (define-class <error> () > (msg #:getter msg #:init-keyword #:msg)) > > (define-class <file-error> (<error>) > (path #:getter path #:init-keyword #:path) > (code #:getter code #:init-keyword #:code) > (fs #:getter fs #:init-keyword #:fs)) > > (define-method (initialize (@ <file-error>) args) > (let-keywords > args #f ((path #f) (code #f) (fs #f)) > (next-method @ (list #:msg (format #f "File error in ~S (~S)" path > code))))) > ======================================================================== > > How do I now write the getters for path, code and fs? > > > Marko > [-- Attachment #2: Type: text/html, Size: 2309 bytes --] ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: GOOPS constructors 2014-07-22 13:09 ` Tobias Brandt @ 2014-07-22 13:15 ` Marko Rauhamaa 0 siblings, 0 replies; 8+ messages in thread From: Marko Rauhamaa @ 2014-07-22 13:15 UTC (permalink / raw) To: Tobias Brandt; +Cc: guile-user@gnu.org Tobias Brandt <tob.brandt@googlemail.com>: > I don't have access to guile currently, so I can't try this. But I > think you can just pass path, code, and fs to next-method. They should > eventually end up in the default initialize. Hey, that was it! Thank you. Marko ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: GOOPS constructors 2014-07-22 13:03 ` Marko Rauhamaa 2014-07-22 13:09 ` Tobias Brandt @ 2014-07-23 15:02 ` Barry Fishman 2014-07-23 17:42 ` Marko Rauhamaa 1 sibling, 1 reply; 8+ messages in thread From: Barry Fishman @ 2014-07-23 15:02 UTC (permalink / raw) To: guile-user On 2014-07-22 16:03:43 +0300, Marko Rauhamaa wrote: > Tobias Brandt <tob.brandt@googlemail.com>: > Well, I was trying to find a simple toy example to demonstrate the need, > but maybe I'll need to find a more compelling one. > > How about: > > ======================================================================== > (define-class <error> () > (msg #:getter msg #:init-keyword #:msg)) > > (define-class <file-error> (<error>) > (path #:getter path #:init-keyword #:path) > (code #:getter code #:init-keyword #:code) > (fs #:getter fs #:init-keyword #:fs)) > > (define-method (initialize (@ <file-error>) args) > (let-keywords > args #f ((path #f) (code #f) (fs #f)) > (next-method @ (list #:msg (format #f "File error in ~S (~S)" path code))))) > ======================================================================== > > How do I now write the getters for path, code and fs? If you need to redefine "initialize" to do what you want, this is a clear indication that you might using the wrong abstraction. You are trying to force methods to be a part of the class definition. They don't belong there. Although this is done in other object oriented languages, this it not a part of CLOS/GOOPS. In GOOPS, methods and data are distinct. Classes form a hierarchy of the types of objects. Methods provide an common interface for a collection of object types. Languages like Java recognize the distinction, but still force everything (including independent functions) into classes. You can build your <shape> class tree to include <rectangle>, <square>, etc. You can add methods for area, circumference, etc. You can make your code a package for others to share, or buy. If I then come along and use your package in a application. I might want to draw out the shapes. My draw methods might be GTK specific. Where does my code belong? You may not want to update your package to include my GTK specific draw functions. In other languages, if I have your source, I can patch your package to include my own draw <shape> virtual method, I will then have to maintain it and track all your bug fixes and changes. I might build a visitor pattern, if you made that possible, but this does require me to track all your future class structure changes. However in GOOPS, I can build my own draw methods for your derived classes along with any other independent packages that I use. I could restrict my methods to use only the access methods you provide for each of the specific classes I support, and avoid internal class information. I don't need to touch your code. GOOPS will auto-generate simple assessors for you, but anything more complex really should not be pushed into the class definition. After having to use gross mechanisms (like visitor patterns or even #ifdefs) in languages like C++, I find this as a significant improvement. If two classes have no common fields, like <rectangle> and <square>, they just share a common interface, they are not derived from each other. They only need a common interface class (like <shape>), if this is required for class inheritance (like being used in higher level method signatures). There is no need to juggle a set of Interface only base classes, and the associated derived classes for a package, each time an application needs a new interface method. Interface classes are just needed for pure class inheritance. Application specific methods can be added without having to modify the class definition in an external package. --8<---------------cut here---------------start------------->8--- (define-class <error> () ) ; No common class fields (define-class <file-error> (<error>) (path #:getter path #:init-keyword #:path) (code #:getter code #:init-keyword #:code) (fs #:getter fs #:init-keyword #:fs)) (define-method (get-message (err <file-error>)) (format #f "File error in ~S (~S)" (path err) (code err))) --8<---------------cut here---------------end--------------->8--- You don't get any "missing method" until run-time, but Scheme is a dynamic language, so this isn't generally true anyway. You gain the ability to make error messages unique in each of several applications that share common error throwing code. (define-method (my-message (err <file-error>)) (format #f "ERROR: Code ~S in ~S" (code err) (path err))) (define-method (my-message (err <error>)) (format #f "ERROR: Unknown error type ~S" err)) -- Barry Fishman ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: GOOPS constructors 2014-07-23 15:02 ` Barry Fishman @ 2014-07-23 17:42 ` Marko Rauhamaa [not found] ` <m3lhri21kk.fsf@barry_fishman.acm.org> 0 siblings, 1 reply; 8+ messages in thread From: Marko Rauhamaa @ 2014-07-23 17:42 UTC (permalink / raw) To: Barry Fishman; +Cc: guile-user Barry Fishman <barry_fishman@acm.org>: > If you need to redefine "initialize" to do what you want, this is a > clear indication that you might using the wrong abstraction. If I have an IS-A relation, that is often described as class hierarchy. The general question here is, is IS-A-WHOSE a valid basis for class derivation? For example, A thunk IS-A function WHOSE argument list is empty. A square IS-A rectangle WHOSE width equals its height. Green light IS electro-magnetic radiation WHOSE wavelength is 520 nm. A Texan IS-AN American WHOSE permanent address is in Texas. In many programming languages, such restrictions are routinely expressed in object constructors: class GreenLight(Radiation): def __init__(self): Radiation.__init__(self, wavelength=520e-9) Marko ^ permalink raw reply [flat|nested] 8+ messages in thread
[parent not found: <m3lhri21kk.fsf@barry_fishman.acm.org>]
* Re: GOOPS constructors [not found] ` <m3lhri21kk.fsf@barry_fishman.acm.org> @ 2014-07-25 10:26 ` Marko Rauhamaa 0 siblings, 0 replies; 8+ messages in thread From: Marko Rauhamaa @ 2014-07-25 10:26 UTC (permalink / raw) To: Barry Fishman; +Cc: guile-user Barry Fishman <barry_fishman@acm.org>: > I am probably not the best person to go into this. I am more familiar > with CLOS. > > As Tobias Brandt previous described, you can do what you want with > overriding the initialization method. I just find the resulting > code harder to read, and conceptually a bit fuzzy. I'm actually making a realization that GOOPS is probably not what I want. The answer is much closer: <URL: https://www.gnu.org/software/guile/manual/html_node/OO-C losure.html#OO-Closure> I have written a tiny (= 61 lines at the moment) object system based on that idea. Namely, I don't need classes, I only need objects and convenient dispatching. Python is notorious for ducktyping: an object is judged by its personal behavior and not by its membership in a class. However, even Python is keen on the "class" keyword, and it assigns each object a type or class. For the most part, Python's classes are equivalent to (constructor) functions. So why not take that principle further and renounce the existence of classes altogether? In practice, Python's classes are very good. The only downside is that when you need to construct one-off objects with special methods, you must first create an one-off inner class and then construct your object. It would be much cleaner and Scheme-like to just construct your one-off object with a lambda constructor that sets the methods without bothering to name a class. This becomes a practical issue in network programming, where you deal with state machines. You could of course organize your state machine matrix in functions that dispatch on states: (define (received-eof) (case state ((#:IDLE) ...) ((#:READY) ...) ((#:SHUTTING-DOWN) ...) (else ...))) (set! state #:IDLE) (which Python couldn't do, BTW). However, I find it elegant to employ the state pattern: (define (<IDLE>) (define (received-eof) ...) (make-object #f #f received-eof))) (set! state (<IDLE>)) where "make-object" is from my tiny object system. What's specially appealing with this kind of object system (as opposed to GOOPS or Python) is that slot handling is more natural, IMO. For example: (define (<point> .x .y) (define (x) .x) (define (y) .y) (define (reset! x y) (set! .x x) (set! .y y)) (define (clear!) (reset! 0 0)) (make-object #f #f x y reset! clear!)) Now, (let ((p (<point> 3 4))) (-> p x)) => 3 (I'm still looking for the most elegant syntax for method dispatching. I don't particularly like the looks of (p 'x). Maybe (p #:x).) Marko ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2014-07-25 10:26 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-07-22 12:18 GOOPS constructors Marko Rauhamaa 2014-07-22 12:22 ` Tobias Brandt 2014-07-22 13:03 ` Marko Rauhamaa 2014-07-22 13:09 ` Tobias Brandt 2014-07-22 13:15 ` Marko Rauhamaa 2014-07-23 15:02 ` Barry Fishman 2014-07-23 17:42 ` Marko Rauhamaa [not found] ` <m3lhri21kk.fsf@barry_fishman.acm.org> 2014-07-25 10:26 ` Marko Rauhamaa
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).