From: Bulent Murtezaoglu
Subject: let/declare curiosity
Date: 
Message-ID: <87bsoahx79.fsf@nkapi.internal>
Actually the hyperspec covers this and I quote from:
http://www.xanalys.com/software_tools/reference/HyperSpec/Body/speope_letcm_letst.html
it says:
---
The code

(let (x)
   (declare (integer x))
   (setq x (gcd y z))
   ...)

is incorrect; although x is indeed set before it is used, and is set
to a value of the declared type integer, nevertheless x initially
takes on the value nil in violation of the type declaration.

---

I understand this, but it bothers me when the binding is for something
like an array or structure and it is not convenient to call
make-mumble in the let (eg suppose you don't know all about the array,
or maybe it needs to be in scope but isn't always created if some loop
loops zero times).  What does one do then?  

Or maybe the question to ask is, what might one be doing wrong if one 
asks this question?  In my case I have a macro that expands into
something like

(let (var)
	(flet ((foo ()
                 ; do something with var ))
        (loop when (somecall)
           do (setf var (make-array 10000))
	;; do other suff to var involving foo
)))	 
 
The somecall predicate might return nil in the first try, so if I
make-array in var's init-form I'll have generated garbage 
just to be able to declare var's type.  I realize I can bind var 
after I know it is necessary and pass it to foo, but was I hoping I 
could avoid that.  Any suggestions?

thanks,

BM








 

From: Kent M Pitman
Subject: Re: let/declare curiosity
Date: 
Message-ID: <sfwwv6yj8bc.fsf@world.std.com>
Bulent Murtezaoglu <··@acm.org> writes:

> (let (var)
> 	(flet ((foo ()
>                  ; do something with var ))
>         (loop when (somecall)
>            do (setf var (make-array 10000))
> 	;; do other suff to var involving foo
> )))	 
>  

The problem has to do with the initial declaration having to be suitable
to say what storage size to allocate.  Other/later declarations only say 
what type to infer from a dispatch/error-checking point of view.
The system has the same problem you do, of needing to allocate an initial
value for the slot but not knowing what to put there.  It has to (or might
have to, depending on the implementation) put some valid data there, 
for the GC's sake.

(let (var)
  (declare (type (or null array) var))
  (flet ((foo ()
                  ; do something with var ))
         (loop when (somecall)
            do (setf var (make-array 10000))
 	;; do other suff to var involving foo
 )))	 

The above should work but won't give you very tight code if the reason
you want to declare this isn't to save space allocating the storage for
var (which almost no extant compiler will optimize anyway) but instead to
affect type checking, etc.  In the latter case, you want to use locally:

(let (var)
  (flet ((foo ()
           (locally (declare (type array var))
                  ; do something with var )))
    (loop when (somecall)
            do (setf var (make-array 10000))
               (locally (declare (type array var))
         	;; do other stuff to var involving foo
                ))))

> The somecall predicate might return nil in the first try, so if I
> make-array in var's init-form I'll have generated garbage 
> just to be able to declare var's type.  I realize I can bind var 
> after I know it is necessary and pass it to foo, but was I hoping I 
> could avoid that.  Any suggestions?

Declarations don't have to go with bindings, and in your case I think 
you don't want them to.
From: Tim Bradshaw
Subject: Re: let/declare curiosity
Date: 
Message-ID: <nkj3d9leui5.fsf@tfeb.org>
Kent M Pitman <······@world.std.com> writes:

> 
> The above should work but won't give you very tight code if the reason
> you want to declare this isn't to save space allocating the storage for
> var (which almost no extant compiler will optimize anyway) but instead to
> affect type checking, etc.  In the latter case, you want to use locally:
> 

Actually, I think that a decent type-inferencing compiler should be OK
with this.  For something like:

(let (x)
  (declare (type (or null my-nice-type) x))
  ...
  (setf x (make-my-nice-type))
  ;; here
  (do-my-nice-type-specific-operation-on x))

then it can infer that at `here' the thing must be a my-nice-type, if it
knows enough about MAKE-MY-NICE-TYPe (maybe) and
DO-MY-NICE-TYPE-SPECIFIC-OPERATION-ON.

This is kind of a vague explanation, it can be made concrete by
looking at the code that CMUCL generates for things like this which is
often OK.  Often it needs to do a typecheck at `here', but in the common case of something like:

(let (x)
  (declare (type (or null my-nice-type) x))
  ...
  (setf x (make-my-nice-type))
  ;; here
  (loop repeat large-number
        do ... (do-my-nice-type-specific-operation-on x) ...))

this is only one typecheck.

(Actually, it only needs one typecheck anyway, the difference is that
if DO-MY-NICE-TYPE-SPECIFIC-OPERATION-ON is something like AREF and
MY-NICE-TYPE is some good array type it can win, I think.  Maybe
someone who udnerstands CMUCL better than I could comment or correct
me).

--tim
From: Raymond Toy
Subject: Re: let/declare curiosity
Date: 
Message-ID: <4nlmndsk7c.fsf@rtp.ericsson.se>
>>>>> "Tim" == Tim Bradshaw <···@tfeb.org> writes:

    Tim> (let (x)
    Tim>   (declare (type (or null my-nice-type) x))
    Tim>   ...
    Tim>   (setf x (make-my-nice-type))
    Tim>   ;; here
    Tim>   (loop repeat large-number
    Tim>         do ... (do-my-nice-type-specific-operation-on x) ...))

    Tim> this is only one typecheck.

    Tim> (Actually, it only needs one typecheck anyway, the difference is that
    Tim> if DO-MY-NICE-TYPE-SPECIFIC-OPERATION-ON is something like AREF and
    Tim> MY-NICE-TYPE is some good array type it can win, I think.  Maybe
    Tim> someone who udnerstands CMUCL better than I could comment or correct
    Tim> me).

It's been my experience that whenever you setf something, CMUCL
doesn't infer anything.  It believes the declaration.

When this is important to me, I usually do something like

(let (x)
  (declare (type (or null my-nice-type) x))
  ...
  (let ((x (make-my-nice-type)))
    ;; here
    (loop repeat large-number
          do ... (do-my-nice-type-specific-operation-on x) ...)))

So I don't need any additional declarations (unless I later setf this
new x, in which case I do), and there isn't even a type-check at here.

I guess a locally form would also suffice.  (But in CMUCL, it always
trusts any declarations in the locally form so no typechecks are done
as is normal in reasonably safe code.  So if you lie, you lose.)

Ray
From: Barry Margolin
Subject: Re: let/declare curiosity
Date: 
Message-ID: <XdsR6.2$Ij4.149@burlma1-snr2>
In article <···············@tfeb.org>, Tim Bradshaw  <···@tfeb.org> wrote:
>Actually, I think that a decent type-inferencing compiler should be OK
>with this.  For something like:
>
>(let (x)
>  (declare (type (or null my-nice-type) x))
>  ...
>  (setf x (make-my-nice-type))
>  ;; here
>  (do-my-nice-type-specific-operation-on x))
>
>then it can infer that at `here' the thing must be a my-nice-type, if it
>knows enough about MAKE-MY-NICE-TYPe (maybe) and
>DO-MY-NICE-TYPE-SPECIFIC-OPERATION-ON.

If an SSC knows that much about MAKE-MY-NICE-TYPE then you wouldn't need
the declaration in the first place.  Flow analysis would allow it to infer
the type all by itself.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Tim Bradshaw
Subject: Re: let/declare curiosity
Date: 
Message-ID: <nkj8zjdqz9i.fsf@tfeb.org>
Barry Margolin <······@genuity.net> writes:

> If an SSC knows that much about MAKE-MY-NICE-TYPE then you wouldn't need
> the declaration in the first place.  Flow analysis would allow it to infer
> the type all by itself.
> 

I think I was thginking of the case where it doesn't know that much
about MAKE-MY-NICE-TYPE, but I'm not sure any more.

--tim
From: Barry Margolin
Subject: Re: let/declare curiosity
Date: 
Message-ID: <sCxR6.16$Ij4.383@burlma1-snr2>
In article <···············@tfeb.org>, Tim Bradshaw  <···@tfeb.org> wrote:
>Barry Margolin <······@genuity.net> writes:
>
>> If an SSC knows that much about MAKE-MY-NICE-TYPE then you wouldn't need
>> the declaration in the first place.  Flow analysis would allow it to infer
>> the type all by itself.
>> 
>
>I think I was thginking of the case where it doesn't know that much
>about MAKE-MY-NICE-TYPE, but I'm not sure any more.

You can't have it both ways.  Either it knows enough to ignore the "OR
NULL" part of the type declaration, in which case it doesn't need any type
declaration, or it doesn't know enough and needs a type declaration that's
specific enough that it can do something useful with it.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Tim Bradshaw
Subject: Re: let/declare curiosity
Date: 
Message-ID: <nkjlmnc8qfq.fsf@tfeb.org>
Barry Margolin <······@genuity.net> writes:

> You can't have it both ways.  Either it knows enough to ignore the "OR
> NULL" part of the type declaration, in which case it doesn't need any type
> declaration, or it doesn't know enough and needs a type declaration that's
> specific enough that it can do something useful with it.
> 

The particular thing I was thinking of was something like this (this
is kind of far from the original example, I'm sorry, and it may not be
relevent to it, but it is what I was thinking of...):

(let ((x nil))
  (setf x (function))
  (dotimes (i (length x) x)
    (setf (aref x i) (round (aref x i) 2))))

For this code the compiler should be able to work out that x needs to
be of type (or null (array number (*))) - even if it knows nothing
about FUNCTION.  So it could compile a check just after the setf to
make sure it is (now) of type (array number (*)).  If I change this to:

(let ((x nil))
  (declare (type (or null (simple-array fixnum (*)))))
  (setf x (function))
  (dotimes (i (length x) x)
    (setf (aref x i) (round (aref x i) 2))))

Then, still without knowing anything about FUNCTION, it can insert a
much more precise check just above the start of the loop and then
likely compile much better code below.

Of course a more interesting case would be when it wasn't (or null
...)  but (or some-type-incompatible-with-the-code some-good-type),
where SOME-GOOD-TYPE is a subtype of what it has already been able to
infer.

Whether CMUCL actually can optimize this case, I'm not sure.

--tim
From: Tim Bradshaw
Subject: Re: let/declare curiosity
Date: 
Message-ID: <nkjitig8pi4.fsf@tfeb.org>
* I wrote:

> 
> (let ((x nil))
>   (setf x (function))
>   (dotimes (i (length x) x)
>     (setf (aref x i) (round (aref x i) 2))))
> 

Obviously I should have chosen a better name than FUNCTION for my
pretend function, sorry!

--tim
From: Reini Urban
Subject: Re: let/declare curiosity
Date: 
Message-ID: <9fd0n2$3or$4@fstgss02.tu-graz.ac.at>
Barry Margolin <······@genuity.net> wrote:
: If an SSC knows that much about MAKE-MY-NICE-TYPE then you wouldn't need
: the declaration in the first place.  Flow analysis would allow it to infer
: the type all by itself.

SSC = "Sufficiently Smart Compiler" ?
or superior?
-- 
Reini Urban
http://xarch.tu-graz.ac.at/acadwiki/AutoLispFaq
From: Barry Margolin
Subject: Re: let/declare curiosity
Date: 
Message-ID: <K6BS6.4$5t5.67@burlma1-snr2>
In article <············@fstgss02.tu-graz.ac.at>,
Reini Urban  <······@x-ray.at> wrote:
>Barry Margolin <······@genuity.net> wrote:
>: If an SSC knows that much about MAKE-MY-NICE-TYPE then you wouldn't need
>: the declaration in the first place.  Flow analysis would allow it to infer
>: the type all by itself.
>
>SSC = "Sufficiently Smart Compiler" ?

Yes.

-- 
Barry Margolin, ······@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Bulent Murtezaoglu
Subject: Re: let/declare curiosity
Date: 
Message-ID: <87zobtgp3o.fsf@nkapi.internal>
Thanks, Kent, for reminding me "locally" (or maybe I should say 
teaching, I'm not sure if I ever used it).  Here's an additional 
curiosity:

    KMP> (let (var) 
    KMP>    (flet ((foo () 
    KMP>              (locally (declare (type array var)) ;;mark
    KMP>                   ; do something with var ))) 
    KMP>   (loop when (somecall) 
    KMP>   do (setf var (make-array 10000)) 
    KMP>     (locally (declare (type array var))
    KMP> ;; do other stuff to var involving foo ))))

Ok, the next question is whether or not the locally inside  
foo is necessary even in the case of a SSC.  Steele has a cute
example in CLtL2 that matches this (p 226, I could not get the 
web version to work for me) and he says you absolutely do since 
the SSC is not allowed to go by the extent of the declaration but 
its scope.  I know CLtL2 is not an authoritative source, but if 
what Steele is saying is in fact the case then I wonder what happens 
when the SSC inlines foo?  It seems that it cannot optimize based
on the declaration from the calling context.  Thinking of inlining 
helped me decide that this was counterintuitive to me, though it is
reasonable behaviour given the extent/scope argument

I dug up the HyperSpec page about this also (which seems to confirm
CLtL2 and uses similar examples):

http://www.xanalys.com/software_tools/reference/HyperSpec/Issues/iss096-writeup.html

Any thoughts?  

BM