Path: csiph.com!news.swapon.de!eternal-september.org!feeder.eternal-september.org!reader01.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.lang.c Subject: Re: [PC] Simulating Monads, Lambdas, Closures, and Laziness Date: Wed, 03 Apr 2019 07:59:37 -0700 Organization: A noiseless patient Spider Lines: 61 Message-ID: <86h8bfm8x2.fsf@mailhub.linuxsc.com> References: <87ef6l72u5.fsf@bsb.me.uk> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="13e1b5f6145a62acfac5bc7ad5b65772"; logging-data="26775"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/S5GgMaWvBntWSLyAiPWOzEkGYNElCPgE=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:ulLwBAPGrJ6DkLI3qenqa8tPivY= sha1:lo53AVL5Ci2AsY8QrB7kzF4jYRw= Xref: csiph.com comp.lang.c:142781 [trimming without any further indication] Ben Bacarisse writes: > M Joshua Ryan writes: > >> typedef union object *object; > > I'm not a fan of hiding a pointer like this. It tripped me up for > a while. For the price of a space and a * you lose the ability to > remind the reader that its pointers all the way down. I have a different view. When the primary type of an interface is a pointer type and not the pointed-to type, it's better for the interface to define a typedef name that includes the *, and use that name in declaring the various interface functions, both for parameters and return values. Client code normally uses the pointer type, not the pointed-to type; following the principle of information hiding, the pointerness is better held inside the typedef name than forcing client code to be reminded at every turn of more information than is needed. There is a question of which type should be taken as "the primary type", especially in settings like this one where there is no explicit line between client code and code for the abstraction (and certainly no clearcut expression of what constitutes "the interface" to the abstraction). But look at the source. There are, not counting the few bottom-level constructor functions, roughly 30 functions. About two-thirds of those don't access the pointed-to internals at all, but just pass around values that come from lower-level functions (or parameters, etc). Most of the code (counting by number of functions) doesn't need to know, or care, that "under the hood" the values are actually pointers. Furthermore any additional code is more likely not to need any internal access either. So it seems fair to say the primary type here is the pointer type rather than the pointed-to type. Incidentally, there is just one function ('new_') that takes the pointed-to type as a parameter. The source is easily changed so new_() takes the pointer type instead (please excuse any changes in whitespace style): object new_( object o ){ object p = calloc( 1, sizeof *p ); return p ? *p = *o, p : 0; } #define OBJECT(...) new_( (union object[]){{ __VA_ARGS__ }} ) For reference, here is the original (modulo whitespace changes): object new_( union object o ){ object p = calloc( 1, sizeof o ); return p ? *p = o, p : 0; } #define OBJECT(...) new_( (union object){ __VA_ARGS__ } ) Callers of new_() go through the OBJECT() macro exclusively, which allows them to work without any further changes (kudos for good factoring; unfortunately a demerit for using 'sizeof o' rather than 'sizeof *p').