From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Mark H Weaver Newsgroups: gmane.lisp.guile.devel Subject: Re: Functional record "setters", a different approach Date: Thu, 12 Apr 2012 11:04:13 -0400 Message-ID: <874nsp800i.fsf@netris.org> References: <87fwcaah4w.fsf@netris.org> <87r4vthpti.fsf@gnu.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Trace: dough.gmane.org 1334243260 32224 80.91.229.3 (12 Apr 2012 15:07:40 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Thu, 12 Apr 2012 15:07:40 +0000 (UTC) Cc: guile-devel@gnu.org To: ludo@gnu.org (Ludovic =?utf-8?Q?Court=C3=A8s?=) Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Thu Apr 12 17:07:39 2012 Return-path: Envelope-to: guile-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1SILcX-0001tw-Ny for guile-devel@m.gmane.org; Thu, 12 Apr 2012 17:07:37 +0200 Original-Received: from localhost ([::1]:42098 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SILcX-0004Me-11 for guile-devel@m.gmane.org; Thu, 12 Apr 2012 11:07:37 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:60208) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SILcU-0004MT-1W for guile-devel@gnu.org; Thu, 12 Apr 2012 11:07:35 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SILcN-0002nO-GQ for guile-devel@gnu.org; Thu, 12 Apr 2012 11:07:33 -0400 Original-Received: from world.peace.net ([96.39.62.75]:51454) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SILcN-0002n2-AS; Thu, 12 Apr 2012 11:07:27 -0400 Original-Received: from 209-6-91-212.c3-0.smr-ubr1.sbo-smr.ma.cable.rcn.com ([209.6.91.212] helo=yeeloong) by world.peace.net with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.72) (envelope-from ) id 1SILcC-0004s9-N2; Thu, 12 Apr 2012 11:07:17 -0400 In-Reply-To: <87r4vthpti.fsf@gnu.org> ("Ludovic \=\?utf-8\?Q\?Court\=C3\=A8s\=22'\?\= \=\?utf-8\?Q\?s\?\= message of "Thu, 12 Apr 2012 00:22:01 +0200") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.92 (gnu/linux) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 96.39.62.75 X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Original-Sender: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.devel:14257 Archived-At: Hi Ludovic! ludo@gnu.org (Ludovic Court=C3=A8s) writes: > Mark H Weaver skribis: >> The public interface I've created is quite a bit different than what >> we've been discussing so far. I'm open to changing it, but here's what >> the attached patch currently exports from (srfi srfi-9 gnu): >> >> (modified-copy ( ) ...) >> (modified-copy-nocheck ( ) ...) >> >> where is of the form ( ...) > > I=E2=80=99d still want named single-field setters, for convenience. For = that, > we probably still need a separate =E2=80=98define-immutable-record-type= =E2=80=99. Agreed. > Also, I=E2=80=99d want to avoid the term =E2=80=98copy=E2=80=99, which is= sounds low-level; > =E2=80=98set=E2=80=99 seems more appropriate to me. WDYT? I agree that 'copy' is not a good term to use here, especially since no copy is made in the zero-modification case of (modified-copy s). However, I find the term 'set' misleading, since no mutation is taking place. Maybe 'update'? I dunno, I don't have strong feelings on this. > Regarding the interface for multi-field nested changes, I=E2=80=99d still > prefer: > > (set-field p (foo bar) val > (foo baz) chbouib) > > Or is it less readable than: > > (set-field p ((foo bar) val) > ((foo baz) chbouib)) I find the first variant to be very un-scheme-like. One could make the same argument about 'let', 'let-values', or 'cond', but the Scheme community has consistently chosen the latter style of syntax. The latter style allows better extensibility. Also, the 'syntax-rules' pattern matching machinery works very nicely with the usual scheme syntax, and poorly for your proposed syntax. I feel fairly strongly about this. > Finally, I think there=E2=80=99s shouldn=E2=80=99t be a =E2=80=98-nocheck= =E2=80=99 version. Dynamic > typing entails run-time type checking, that=E2=80=99s a fact of life, but= safety > shouldn=E2=80=99t have to be traded for performance. Hmm. I agree that the 'nocheck' variant should not be prominently mentioned, and perhaps not documented at all, but I suspect it will prove useful to keep it around, even if only for our own internal use to build efficient higher-level constructs. For example, when I built this 'modified-copy' machinery, I was unable to build upon the usual ( s) syntax, because that would cause the generated code to include many redundant checks (one for each field retrieved). For example, for (modified-copy s ((foo-x) 'new)) where 's' contains 10 fields, the expanded code would include 9 separate checks that 's' is the right type. Even when our compiler becomes smart enough to eliminate those redundant checks, it creates a lot of extra work for the compiler, slowing everything down, and of course when interpreting (or compiling without optimization) it is a serious lose. Therefore, I needed a 'nocheck' version of the individual getters, so that I could check the type just once and then fetch each individual field without additional checks. Unfortunately, there was none, so I needed I hack around this limitation, adding a mechanism to retrieve the field-index from a getter at expansion time, and then using 'struct-ref' directly. This is ugly. I'd have preferred to have a 'nocheck' getter instead. In summary, my view is that in order to enable practical elegant programming for users, we sometimes need to do less elegant (or even downright ugly) things in the lower levels. Otherwise the system will be too slow, and users will reject elegant constructs such as functional setters and just use plain mutation instead. >> These macros can be used on _any_ srfi-9 record, not just ones specially >> declared as immutable. > > I assume this preserves =E2=80=9CABI=E2=80=9D compatibility too, right? Yes. > However, in the future, could you please reply in the same thread, You're right, I should have done so. > and more importantly coordinate so we don=E2=80=99t waste time working on= the > same code in parallel. I started this work after you were (probably) asleep, and rushed to post about it before you woke up, so I did my best there. If you would prefer to use your own code instead, that's okay with me. As long as we end up with a functional-multi-setter that generates good code, I'll be satisfied. > FWIW I was using this approach to represent the tree of accessors: >=20 > (define (field-tree fields) > ;; Given FIELDS, a list of field-accessor-lists, return a tree > ;; that groups together FIELDS by prefix. Example: > ;; FIELDS: ((f1 f2 f3) (f1 f4)) > ;; RESULT: ((f1 (f2 (f3)) (f4))) > (define (insert obj tree) > (match obj > ((head tail ...) > (let ((sub (or (assoc-ref tree head) '()))) > (cons (cons head (insert tail sub)) > (alist-delete head tree)))) > (() > tree))) >=20 > (fold-right insert '() fields)) I agree that this is much nicer than my corresponding code. Thanks for sharing. Would you like me to incorporate something like this into my code, or would you like to start with your code and maybe cherry-pick ideas/code from mine? Either way is fine with me. Thanks! Mark