unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* GOOPS functional setter
@ 2017-01-13 19:09 Christopher Allan Webber
  2017-01-13 20:56 ` tomas
  2017-01-13 21:33 ` Jan Nieuwenhuizen
  0 siblings, 2 replies; 8+ messages in thread
From: Christopher Allan Webber @ 2017-01-13 19:09 UTC (permalink / raw)
  To: Guile user

I guess I never sent this to this list.  Here's a functional setter for
GOOPS classes.

Maybe nobody else would use it, but I've used it in a couple of projects
now, so figure I might as well share.

This is LGPLv3+.  Feel free to use.

==================================================

;; By Christopher Allan Webber, LGPLv3+; adapted from shallow-clone in GOOPS
(use-modules (oop goops))

(define-method (slot-fset (self <object>) slot-name value)
  "Return a new copy of SELF, with all slots preserved except SLOT-NAME
set to VALUE."
  (let* ((class (class-of self))
         (clone (allocate-instance class '())))
    (for-each (lambda (slot)
                (define slot-n
                  (slot-definition-name slot))
                (if (and (not (eq? slot-n slot-name)) (slot-bound? self slot-n))
                    (slot-set! clone slot-n (slot-ref self slot-n))))
              (class-slots class))
    ;; Set the particular slot we're overriding
    (slot-set! clone slot-name value)
    clone))

==================================================

(... Would anyone find this useful to include in Guile itself?  It might
also be nice to have a version that sets multiple fields at once, like
set-fields.  I'm not sure what the syntax should look like for that
though.  Maybe keep it simple?)

  (slot-multi-fset foo
    slot-name slot-val
    slot-name2 slot-val2)



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-13 19:09 GOOPS functional setter Christopher Allan Webber
@ 2017-01-13 20:56 ` tomas
  2017-01-14  2:11   ` Christopher Allan Webber
  2017-01-13 21:33 ` Jan Nieuwenhuizen
  1 sibling, 1 reply; 8+ messages in thread
From: tomas @ 2017-01-13 20:56 UTC (permalink / raw)
  To: guile-user

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Fri, Jan 13, 2017 at 01:09:57PM -0600, Christopher Allan Webber wrote:
> I guess I never sent this to this list.  Here's a functional setter for
> GOOPS classes.
> 
> Maybe nobody else would use it, but I've used it in a couple of projects
> now, so figure I might as well share.
> 
> This is LGPLv3+.  Feel free to use.
> 
> ==================================================
> 
> ;; By Christopher Allan Webber, LGPLv3+; adapted from shallow-clone in GOOPS
> (use-modules (oop goops))
> 
> (define-method (slot-fset (self <object>) slot-name value)
>   "Return a new copy of SELF, with all slots preserved except SLOT-NAME
> set to VALUE."

(from the peanut gallery)

I understand now what it does, but the name "slot-fset" would confuse the heck
of me. Why not something like "clone" or perhaps "clone*"?

regards
- -- tomás
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iEYEARECAAYFAlh5Pw4ACgkQBcgs9XrR2kaNewCfSb5g/bMiYZH5ITIgmqm4IIUa
x9gAn33QPd+Bwr5ivPE1t4C/35jX1/ZK
=a0gw
-----END PGP SIGNATURE-----



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-13 19:09 GOOPS functional setter Christopher Allan Webber
  2017-01-13 20:56 ` tomas
@ 2017-01-13 21:33 ` Jan Nieuwenhuizen
  1 sibling, 0 replies; 8+ messages in thread
From: Jan Nieuwenhuizen @ 2017-01-13 21:33 UTC (permalink / raw)
  To: Christopher Allan Webber; +Cc: Guile user

Christopher Allan Webber writes:

> I guess I never sent this to this list.  Here's a functional setter for
> GOOPS classes.

Hah!  Just this week I have been wondering the perceived absense of
functional setters (and figured I *must* have missed something
somewhere) and wrote

--8<---------------cut here---------------start------------->8---
(define ((identity-initializer o) name)
  (list (symbol->keyword name) (slot-ref o name))) 

(define-method (clone o . setters)
 (let* ((class (class-of o))
        (slots (class-slots class))
        (names (map slot-definition-name slots))
        (initializers (map (identity-initializer o) names))
        (keywords (filter keyword? setters))
        (initializers (filter (lambda (i) (not (memq (car i) keywords))) initializers))
        (initializers (append (apply append initializers) setters))
        (arguments (cons class initializers)))
   (apply make arguments)))
--8<---------------cut here---------------end--------------->8---

Usage:

   (clone o #:slot <VALUE> ...)
   
Greetings,
Jan

-- 
Jan Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond http://lilypond.org
Freelance IT http://JoyofSource.com | Avatar®  http://AvatarAcademy.nl  



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-13 20:56 ` tomas
@ 2017-01-14  2:11   ` Christopher Allan Webber
  2017-01-14 10:08     ` tomas
  0 siblings, 1 reply; 8+ messages in thread
From: Christopher Allan Webber @ 2017-01-14  2:11 UTC (permalink / raw)
  To: tomas; +Cc: guile-user

tomas@tuxteam.de writes:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On Fri, Jan 13, 2017 at 01:09:57PM -0600, Christopher Allan Webber wrote:
>> I guess I never sent this to this list.  Here's a functional setter for
>> GOOPS classes.
>>
>> Maybe nobody else would use it, but I've used it in a couple of projects
>> now, so figure I might as well share.
>>
>> This is LGPLv3+.  Feel free to use.
>>
>> ==================================================
>>
>> ;; By Christopher Allan Webber, LGPLv3+; adapted from shallow-clone in GOOPS
>> (use-modules (oop goops))
>>
>> (define-method (slot-fset (self <object>) slot-name value)
>>   "Return a new copy of SELF, with all slots preserved except SLOT-NAME
>> set to VALUE."
>
> (from the peanut gallery)
>
> I understand now what it does, but the name "slot-fset" would confuse the heck
> of me. Why not something like "clone" or perhaps "clone*"?
>
> regards
> - -- tomás

I think cloning isn't as clear; what we want is something that's the
same as the previous instance of the object, but with one field changed.
Clone makes it sound the same, but the goal is change without affecting
the original.  It's attempting to be the same as slot-set! but
functional, hence slot-fset.  Again, inspired by set-field and
set-fields from (srfi srfi-9 gnu).

However, I'm open to another name, if someone has something better...

(Also, it would even be nicer if it were possible to make the new
instance without copying every field manually as I did here.  It would
be interesting if there were a metaclass that was smarter about slot
allocation and used some functional structure so that we didn't need to
iterate through every field just to change the one slot.  I'm not sure
how hard that would be to do, or if it would be of significant benefit
in the end.)



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-14  2:11   ` Christopher Allan Webber
@ 2017-01-14 10:08     ` tomas
  2017-01-14 17:25       ` Arne Babenhauserheide
  2017-01-14 21:16       ` Christopher Allan Webber
  0 siblings, 2 replies; 8+ messages in thread
From: tomas @ 2017-01-14 10:08 UTC (permalink / raw)
  To: Christopher Allan Webber; +Cc: guile-user

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Fri, Jan 13, 2017 at 08:11:28PM -0600, Christopher Allan Webber wrote:
> tomas@tuxteam.de writes:

[fset vs clone]

> I think cloning isn't as clear; what we want is something that's the
> same as the previous instance of the object, but with one field changed.
> Clone makes it sound the same, but the goal is change without affecting
> the original.  It's attempting to be the same as slot-set! but
> functional, hence slot-fset.  Again, inspired by set-field and
> set-fields from (srfi srfi-9 gnu).

Yes, and after having read your post I understood where you came from.
The naming wouldn't have been discovrable for me right off.

Curiously, Jan (also in this thread) came up with "clone",
independently.

Now I'm not trying to imply that "clone" is "more right"; actually
I believe that there are two "modes" at work here. Let me speculate
a bit:

Perhaps from the more "strictly functional" point of view, the clone
operation is less important, because, whether the thing is cloned
behind the scenes or things are arranged by deep compiler magic and
the clone doesn't happen after all is none of our business. Not the
cloning is important, but the changed fields. In this world, the
mutating counterpart (set) doesn't even exists. Clone wouldn't be
an appropriate name here.

- From the more "naive", "imperative" point of view, it's the clone
operation what keeps us awake: allocating memory and things. Here
"clone" is the right word, it seems.

Perhaps what irritates me most is that "fset" is named  after
an imperative operation (set) and lives in a functional world.
Or something.

> However, I'm open to another name, if someone has something better...

At the end it's not that important: you made the function, you name
it :)

If my questions elicit some resonance in you, then they might be
useful.

> (Also, it would even be nicer if it were possible to make the new
> instance without copying every field manually as I did here.  It would
> be interesting if there were a metaclass that was smarter about slot
> allocation and used some functional structure so that we didn't need to
> iterate through every field just to change the one slot.  I'm not sure
> how hard that would be to do, or if it would be of significant benefit
> in the end.)

That's the more important part: the *interface* makes those things
possible in the first place (whether by a more efficient clone
primitive, as you envision, or by a clever compiler or both). Thus
a Good Thing, regardless of its name :)

thanks
- -- tomás
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iEYEARECAAYFAlh5+KcACgkQBcgs9XrR2kaDDACcDLOi8W1gS9TZ0oU4w1bxswAU
OgUAn0HAN1krKDEhJads7v9nIrq7mXRg
=7b9N
-----END PGP SIGNATURE-----



^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-14 10:08     ` tomas
@ 2017-01-14 17:25       ` Arne Babenhauserheide
  2017-01-14 21:16       ` Christopher Allan Webber
  1 sibling, 0 replies; 8+ messages in thread
From: Arne Babenhauserheide @ 2017-01-14 17:25 UTC (permalink / raw)
  To: tomas; +Cc: guile-user

[-- Attachment #1: Type: text/plain, Size: 1720 bytes --]


tomas@tuxteam.de writes:
> [fset vs clone]
>
>> I think cloning isn't as clear; what we want is something that's the
>> same as the previous instance of the object, but with one field changed.

> Yes, and after having read your post I understood where you came from.
> The naming wouldn't have been discovrable for me right off.
> Now I'm not trying to imply that "clone" is "more right"; actually
> I believe that there are two "modes" at work here. Let me speculate
> a bit:
>
> Perhaps from the more "strictly functional" point of view, the clone
> operation is less important, because, whether the thing is cloned
> behind the scenes or things are arranged by deep compiler magic and
> the clone doesn't happen after all is none of our business. Not the
> cloning is important, but the changed fields. In this world, the
> mutating counterpart (set) doesn't even exists. Clone wouldn't be
> an appropriate name here.
>
> - From the more "naive", "imperative" point of view, it's the clone
> operation what keeps us awake: allocating memory and things. Here
> "clone" is the right word, it seems.
>
> Perhaps what irritates me most is that "fset" is named  after
> an imperative operation (set) and lives in a functional world.
> Or something.

If I did not need a short name, I’d use something like
slot-copy-with-changed. I had an immutable datatype in Python where I
used something equivalent to

(changed event
  #:<slot-to-change> new-value
  #:<other-slot-to-change> other-new-value)

I know that this does not provide a better name by itself, but I hope it
helps finding one.

Best wishes,
Arne
-- 
Unpolitisch sein
heißt politisch sein
ohne es zu merken

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 800 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-14 10:08     ` tomas
  2017-01-14 17:25       ` Arne Babenhauserheide
@ 2017-01-14 21:16       ` Christopher Allan Webber
  2017-01-15  9:31         ` tomas
  1 sibling, 1 reply; 8+ messages in thread
From: Christopher Allan Webber @ 2017-01-14 21:16 UTC (permalink / raw)
  To: tomas; +Cc: guile-user

[-- Attachment #1: Type: text/plain, Size: 1145 bytes --]

tomas@tuxteam.de writes:

> Curiously, Jan (also in this thread) came up with "clone",
> independently.

Yes you're right. :)

Speaking of Jan and I both thinking about clone'ish things, we did a bit
of talking on IRC and I think we have a very nice version of functional
setters where you can "clone" multiple fields at the same time.

Here's what it looks like in practice, adapting from the
(srfi srfi-9 gnu) code:

  (define fsf-address
    (make <address>
      #:street "Franklin Street"
      #:city "Boston"
      #:country "USA"))

  (define rms
    (make <person>
      #:age 30
      #:email "rms@gnu.org"
      #:address fsf-address))

  (define new-rms
    (clone rms
           ((.age) 60)
           ((.address .street) "Temple Place")))

  scheme@(guile-user)> (.age rms)
  $12 = 30
  scheme@(guile-user)> (.age new-rms)
  $13 = 60
  scheme@(guile-user)> (.street (.address rms))
  $14 = "Franklin Street"
  scheme@(guile-user)> (.street (.address new-rms))
  $15 = "Temple Place"

... not bad, eh?

Updated copy of goops-functional-setter.scm attached!  What do other
people think?  Should I try to get this upstream in Guile?


[-- Attachment #2: goops-functional-setter.scm --]
[-- Type: text/plain, Size: 2764 bytes --]

;; By Christopher Allan Webber, LGPLv3+; adapted from shallow-clone in GOOPS
(use-modules (oop goops)
             (ice-9 match))

(define-method (slot-fset (self <object>) slot-name value)
  "Return a new copy of SELF, with all slots preserved except SLOT-NAME
set to VALUE."
  (let* ((class (class-of self))
         (clone (allocate-instance class '())))
    (for-each (lambda (slot)
                (define slot-n
                  (slot-definition-name slot))
                (if (and (not (eq? slot-n slot-name)) (slot-bound? self slot-n))
                    (slot-set! clone slot-n (slot-ref self slot-n))))
              (class-slots class))
    ;; Set the particular slot we're overriding
    (slot-set! clone slot-name value)
    clone))

\f
;; By Christopher Allan Webber, LGPLv3+
;; Inspired by a conversation with Jan Nieuwenhuizen... thanks for the
;; help, Jan!
;; This one does an "immutable" interface cloned-with-adjustments
;; version of things that can change multiple fields at the same time.
;; It uses, and requires, accessors to work on the adjusted fields.

(use-modules (oop goops)
             (ice-9 match))

(define (do-clone obj adjust-fields)
  (define new (shallow-clone obj))
  (for-each
   (match-lambda
     ;; Apply just this one field
     (((accessor) val)
      (set! (accessor new) val))
     ;; Recursively apply fields
     (((accessor recur-fields ...) val)
      (set! (accessor new)
            (do-clone (accessor new)
                      (list (list recur-fields val))))))
   adjust-fields)
  new)

(define-syntax-rule (clone obj ((fields ...) val) ...)
  (do-clone obj
            (list (list (list fields ...) val) ...)))

;; That's all the code.
;; Now here's an example adapted from the (srfi srfi-9 gnu)
;; documentation.

(define-class <address> ()
  (street #:init-keyword #:street
          #:accessor .street)
  (city #:init-keyword #:city
        #:accessor .city)
  (country #:init-keyword #:country
           #:accessor .country))

(define-class <person> ()
  (age #:init-keyword #:age
       #:accessor .age)
  (email #:init-keyword #:email
         #:accessor .email)
  (address #:init-keyword #:address
           #:accessor .address))


(define fsf-address
  (make <address> 
    #:street "Franklin Street"
    #:city "Boston"
    #:country "USA"))

(define rms
  (make <person>
    #:age 30
    #:email "rms@gnu.org"
    #:address fsf-address))

(define new-rms
  (clone rms 
         ((.age) 60)
         ((.address .street) "Temple Place")))

;; scheme@(guile-user)> (.age rms)
;; $12 = 30
;; scheme@(guile-user)> (.age new-rms)
;; $13 = 60
;; scheme@(guile-user)> (.street (.address rms))
;; $14 = "Franklin Street"
;; scheme@(guile-user)> (.street (.address new-rms))
;; $15 = "Temple Place"


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: GOOPS functional setter
  2017-01-14 21:16       ` Christopher Allan Webber
@ 2017-01-15  9:31         ` tomas
  0 siblings, 0 replies; 8+ messages in thread
From: tomas @ 2017-01-15  9:31 UTC (permalink / raw)
  To: Christopher Allan Webber; +Cc: guile-user

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Sat, Jan 14, 2017 at 03:16:10PM -0600, Christopher Allan Webber wrote:
> tomas@tuxteam.de writes:
> 
> > Curiously, Jan (also in this thread) came up with "clone",
> > independently.
> 
> Yes you're right. :)
> 
> Speaking of Jan and I both thinking about clone'ish things [...]

> Here's what it looks like in practice, adapting from the
> (srfi srfi-9 gnu) code:
> 
>   (define fsf-address
>     (make <address>
>       #:street "Franklin Street"
>       #:city "Boston"
>       #:country "USA"))
> 
>   (define rms
>     (make <person>
>       #:age 30
>       #:email "rms@gnu.org"
>       #:address fsf-address))
> 
>   (define new-rms
>     (clone rms
>            ((.age) 60)
>            ((.address .street) "Temple Place")))
> 
>   scheme@(guile-user)> (.age rms)
>   $12 = 30
>   scheme@(guile-user)> (.age new-rms)
>   $13 = 60
>   scheme@(guile-user)> (.street (.address rms))
>   $14 = "Franklin Street"
>   scheme@(guile-user)> (.street (.address new-rms))
>   $15 = "Temple Place"
> 
> ... not bad, eh?

You folks rock, seriously :-)

> Updated copy of goops-functional-setter.scm attached!  What do other
> people think?  Should I try to get this upstream in Guile?

I'd love that kind of interface. Feels pretty natural.

regards
- -- t
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.12 (GNU/Linux)

iEYEARECAAYFAlh7QV8ACgkQBcgs9XrR2kbd5wCfVghdRDiJG5/AfrQ0kWkkNBzC
aZIAn3SFxYRIe4C5pG5LpEzsG2dunJgf
=i7FL
-----END PGP SIGNATURE-----



^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2017-01-15  9:31 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-01-13 19:09 GOOPS functional setter Christopher Allan Webber
2017-01-13 20:56 ` tomas
2017-01-14  2:11   ` Christopher Allan Webber
2017-01-14 10:08     ` tomas
2017-01-14 17:25       ` Arne Babenhauserheide
2017-01-14 21:16       ` Christopher Allan Webber
2017-01-15  9:31         ` tomas
2017-01-13 21:33 ` Jan Nieuwenhuizen

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