given a type like this one: (and symbol (satisfies fboundp)).
Would you think that
(typep 3 '(and symbol (satisfies fboundp)))
can signal an error?
In this case the order of type checks is important. (typep 3 'symbol) is fine. But (typep 3 '(satisfies fboundp)) would call (fboundp 3), which would lead to an error.
Is a conforming implementation of ANSI Common Lisp allowed to run the tests in any order for AND types? Will all test be running? From left to right until a test fails?
Above could be replaced by
(typep 3 '(satisfies symbol-fbound-p)
given that symbol-fbound-p is a function that checks for symbolp and fboundp.
In article <ff325b56-310c-4b7f-ad4d-80fe7072d...@q9g2000yqc.googlegroups.com>, Willem Broekema <metaw...@gmail.com> wrote:
> On Dec 5, 10:35 pm, Juanjo <juanjose.garciarip...@googlemail.com> > wrote: > > I would say the CLHS has an implicit evaluation order. This shows that > > http://www.lispworks.com/documentation/HyperSpec/Body/t_satisf.htm > > The example (typep x '(and integer (satisfies evenp))) suggests > > precisely that.
On 2008-12-05, Rainer Joswig <jos...@lisp.de> wrote:
> Hi,
> given a type like this one: (and symbol (satisfies fboundp)).
> Would you think that
> (typep 3 '(and symbol (satisfies fboundp)))
> can signal an error?
> In this case the order of type checks is important.
Unfortunately, the spec basically says that
(and t1 t2 ... tn)
simply ``denotes'' the domain of objects which are in the intersection of those two sets.
I see the more serous issue as being the behavior of SATISFIES. Actually two issues.
Issue 1.
If the given function blows up if given the object as an argument, then obviously, that object does not satisfy that function! So SATISFIES ought to trap the error and return NIL.
But maybe there are good reasons not to do that.
One good reason is that maybe you can use SATISFIES as a building block for implementing your own flavor of the operator which does have that behavior. If SATISFIES caught errors, you woudl not be able to use it as a building block to make a flavor of SATISFIES which passes thorugh errors.
This brings us to:
Issue 2.
The argument of SATISFIES must be a symbol, and not just anything that can serve as a function name. In particular, lambda expressions are explicitly disallowed.
See, if you could write (satisfies (lambda (x) ...)), you'd have it made, thanks to this deftype:
If a symbol with a global function bidning it wants, a symbol with a global function binding it gets! :)
(typep 3 '(satisfies* fboundp)) -> NIL
Still, I have a small problem with the little fact that each occurence of '(satisfies* fboundp) generates a SATISFIES with a different symbol, i.e. two compound forms that are not EQUAL! That's bound to screw something up.
Like when types are compared, how is that done? It's certainly not going make inferences about what the SATISIFED functions do. Type comparison is probably based on the simple structural equivalence of the expanded forms.
Indeed, in this here CLISP implementation, we have a problem:
(subtypep '(satisfies fboundp) '(satisfies fboundp)) -> T
Whoops! The hack must be even more skanky to make it work; instead of using a GENSYM, it should use an interned symbol. We can use a dedicated package for these:
In article <20081221024151....@gmail.com>, Kaz Kylheku <kkylh...@gmail.com> wrote:
> Issue 1.
> If the given function blows up if given the object as an argument, then > obviously, that object does not satisfy that function! So SATISFIES ought to > trap the error and return NIL.
Not necessarily. It means that you can't tell if the object satisfies the function. (SATISFIES IS-LOG-OF-MILLIONTH-FACTORIAL) might exceed the maximum size of bignums while calculating 1,000,000!, but that doesn't mean the parameter isn't ln(1000000!).
-- Barry Margolin, bar...@alum.mit.edu Arlington, MA *** PLEASE post questions in newsgroups, not directly to me *** *** PLEASE don't copy me on replies, I'll read them in the group ***
The Scieneer CL interprets the 'and compound type specifier as intersection and the type may be rewritten or tested in any order. The type system tries hard to simplify compound types and the compiler makes good use of the type information.
This does require that the function of a 'satisfies type specifier can accept objects of any type. It is recommended to still use a compound type when it exposes more type information, for example:
Then use the type '(and symbol (satisfies safe-fboundp)).
It may be possible to rework the type algorithms to maintain the ordering constraint but there are many obvious algorithms that do not maintain this constraint.
Some CL implementations just ignore an error and return false upon an error, but then real errors are lost.
Regards Douglas Crosher
On Dec 6, 8:19 am, Rainer Joswig <jos...@lisp.de> wrote:
> given a type like this one: (and symbol (satisfies fboundp)).
> Would you think that
> (typep 3 '(and symbol (satisfies fboundp)))
> can signal an error?
> In this case the order of type checks is important. > (typep 3 'symbol) is fine. > But (typep 3 '(satisfies fboundp)) would call > (fboundp 3), which would lead to an error.
> Is a conforming implementation of ANSI Common Lisp > allowed to run the tests in any order for > AND types? Will all test be running? From left to > right until a test fails?
> Above could be replaced by
> (typep 3 '(satisfies symbol-fbound-p)
> given that symbol-fbound-p is a function > that checks for symbolp and fboundp.
On 2008-12-06, Barry Margolin <bar...@alum.mit.edu> wrote:
> In article <20081221024151....@gmail.com>, > Kaz Kylheku <kkylh...@gmail.com> wrote:
>> Issue 1.
>> If the given function blows up if given the object as an argument, then >> obviously, that object does not satisfy that function! So SATISFIES ought to >> trap the error and return NIL.
> Not necessarily. It means that you can't tell if the object satisfies > the function. (SATISFIES IS-LOG-OF-MILLIONTH-FACTORIAL) might exceed > the maximum size of bignums while calculating 1,000,000!, but that > doesn't mean the parameter isn't ln(1000000!).
But sometimes it does mean ``this object doesn't satisfy the function''.
Aha, these situations should be distinguishable from each other by the kind of condition: type-error versus some other error.
> The Scieneer CL interprets the 'and compound type specifier as > intersection and the type may be rewritten or tested in any order. > The type system tries hard to simplify compound types and the > compiler makes good use of the type information.
> This does require that the function of a 'satisfies type specifier > can accept objects of any type. It is recommended to still use > a compound type when it exposes more type information, for example:
> Then use the type '(and symbol (satisfies safe-fboundp)).
> It may be possible to rework the type algorithms to maintain > the ordering constraint but there are many obvious algorithms > that do not maintain this constraint.
> Some CL implementations just ignore an error and return false > upon an error, but then real errors are lost.
> Regards > Douglas Crosher
Hi Douglas,
it would interest me what lead to the current version in ANSI CL? Was it a change from CLtL2 or an omission? Two of the ANSI CL examples don't seem to fit into the ANSI CL version.
There is the issue of backward compatibility, since CLtL2 described something different.
From what I got as feedback and from what I read in ANSI CL, it seems that your interpretation is a) 'legal' and b) the rules have been relaxed (from CLtL2 to ANSI CL) to allow such an interpretation (this is what I guess).
So, it looks like 'we' will have to change the CL-HTTP code from (and symbol (satisfies fboundp)) to something like (and symbol (satisfies safe-fboundp)) as a type specifier.
> On Dec 6, 8:19 am, Rainer Joswig <jos...@lisp.de> wrote: > > Hi,
> > given a type like this one: (and symbol (satisfies fboundp)).
> > Would you think that
> > (typep 3 '(and symbol (satisfies fboundp)))
> > can signal an error?
> > In this case the order of type checks is important. > > (typep 3 'symbol) is fine. > > But (typep 3 '(satisfies fboundp)) would call > > (fboundp 3), which would lead to an error.
> > Is a conforming implementation of ANSI Common Lisp > > allowed to run the tests in any order for > > AND types? Will all test be running? From left to > > right until a test fails?
> > Above could be replaced by
> > (typep 3 '(satisfies symbol-fbound-p)
> > given that symbol-fbound-p is a function > > that checks for symbolp and fboundp.
On Dec 6, 4:31 am, Rainer Joswig <jos...@lisp.de> wrote:
> > > Is a conforming implementation of ANSI Common Lisp > > > allowed to run the tests in any order for > > > AND types? Will all test be running? From left to > > > right until a test fails?
I don't have time to research what the standard says. I'll assume lots of people can competently do that.
I don't recall there having been an explicit request to change this, so if in fact it was stronger under CLTL [note that CLTL2 is not in the pedigree of ANSI CL--CLTL2 is a "rogue document", if you'll pardon the colorful characterization] and no one can find a clean up issue (there probably isn't one, but maybe I'm forgetting one), then you can blame it on an editing error. That doesn't mean the error goes away, but you at least know someone to frown at.
Nonetheless, having already accepted the possible guilt for causing the problem, I hope you'll now allow me to go ahead and argue that this shouldn't be treated as a bug without criticizing me for trying to defend my good name (now already sullied :).
I will say that if I were an implementation, I would treat everything to the left of a satisfies as something that must be done first, left- to-right, for pragmatic reasons, even if the standard didn't require it, as long as the standard doesn't forbid it (which I'm pretty sure it doesn't). There are several reasons:
(1) It's likely to be the case that the function call is more expensive. The typechecks should be done first in a lot of cases, and the ordering of the tests should presumably give you a cue of what is least to most expensive to do. I think doing them in arbitrary order isn't meaningful if you are not inlining everything, which is presumably rare. You may be able to guess the speed of the typechecks, but since the satisfies functions will be opaque, you've got to trust the programmer.
(2) A great many functions useful as predicates are not total functions and have type restrictions. oddp, plusp, etc. Those functions become unuseful in this context if not allowed to be ordered. If you do that, the usefulness of AND then becomes quite a lot less. The primary idioms of use for AND in any combination with SATISFIES are almost always to guard a predicate with appropriate type checks, so to claim you can do a SATISFIES in any order is marginal. (Note that nothing requires you to not mix tests where the user can't tell. You can do (AND FOO BAR (SATISFIES BAZ)) in either order for FOO and BAR because if the answer will be undetectably different it's just a proper optimization... assuming FOO and BAR are not deftypes expanding into other AND and SATISFIES, I mean. It's when a user- visible effect happens that you mustn't shuffle things, and that's where SATISFIES happens.
(3) I totally dislike the notion that someone has some pet optimization and that all language semantics have to bend to that optimization. Admittedly, this isn't specified, so you can claim it's not part of the semantics, but it will take you little effort to realize that it was a meta-feature of the language design that we consistently made the left-to-right decision rather than the arbitrary- order decision. That's why we didn't make things non-deterministic in class precedence lists, that's why we did floating point contagion as we did, that's why the function call argument order evaluation is as it is, that's why defsetf and a ton of related mechanism is as it is, ... so why would we throw that away in this one case? Optimizations should suit the language; languages should try not to be merely bizarre syntaxes for asking for particular compiler optimizations.
(4) If there is a history of a previous interpretation and no evidence of cleanup on the matter and there's a lot of code already deployed that seems to interpret this a certain way, I think it's disruptive to the normal state of affairs for a bunch of people with an alternate interpretation that's clever but not evidenced as "relied upon" to move in and say everyone else should rewrite their code just for this. It's not been demonstrated that the relevant efficiency cannot be achieved another way.
(5) If I could take a vote today, it would not be in support of allowing arbitrary ordering, and that would have nothing to do with legacy--it would have to do with the global design principle mentioned in (3).
But that's just my personal opinion and has no official status. Your mileage may vary. :)
> So, it looks like 'we' will have to change the CL-HTTP code > from (and symbol (satisfies fboundp)) > to something like (and symbol (satisfies safe-fboundp)) > as a type specifier.
Well, admittedly that's safest. But it seems like giving up without a fight. If you go that route, I think you're better just making safe- fbound-symbol-p and using a single satisfies without the AND, so the issue doesn't come up. Which is my whole point about AND. If every use has to be written to assume it's not going to do this, that reduces it to near uselessness. You're better off insisting that implementations just do something reasonable.
> (4) If there is a history of a previous interpretation and no evidence > of cleanup on the matter and there's a lot of code already deployed > that seems to interpret this a certain way, I think it's disruptive to > the normal state of affairs for a bunch of people with an alternate > interpretation that's clever but not evidenced as "relied upon" to > move in and say everyone else should rewrite their code just for > this. It's not been demonstrated that the relevant efficiency cannot > be achieved another way.
In at least one part of the lisp world you have this exactly backwards: historically the Python compiler from CMU as treated intersection types as set intersections, and may very well end out moving things around inside of AND types. With a history of that implementation and no cleanup on the matter, I see no justification for expecting a left-to-right order of tests in type testing.
And it's a good thing that there isn't such a requirement, because it would make a mess out of the type system. As it is, intersection types can be simplified -- with a left-to-right ordering this could become absurdly complex. Take for example:
(and number (satisfies evenp) ... (and ... unsigned-byte ...) ... (and ... fixnum ...) ... (and ... bit ...))
Do you really want to prohibit simplifying that to
(and bit (satisfies evenp) ...)
or even (eql 0) or nil, depending on what's in the ... ?
The fact that Python internally uses the Common Lisp type system is IMO an important testament to its being well designed. It's far from perfect, but it's a good enough design that it can actually serve as the basis for a type-smart compiler. Adding a left-to-right requirement would at least place that in doubt.
The suggested requirement on the 'satisfies type predicate simply seeks to maintain the commutative property within the intersection and union types. This is a very useful property both for the programmer and the compiler. No other types in Common Lisp break this property.
For example: if the commutative property is not preserved then given that (subtypep type1 '(and a b (satisfies c))) is a valid test then the following become erroneous: (subtypep type1 '(and b (satisfies c)))
The proposed solution simply requires the predicate to accept all objects without error.
Keep in mind that types are used for both type tests and also for declaration and I believe your comments address only the former and thus miss other valid points of view.
The 'satisfies type is not used widely in declarations because it is of limited use for compiler optimization and commutativity is important to simple design.
> On Dec 6, 4:31 am, Rainer Joswig <jos...@lisp.de> wrote:
> > > > Is a conforming implementation of ANSI Common Lisp > > > > allowed to run the tests in any order for > > > > AND types? Will all test be running? From left to > > > > right until a test fails?
> I don't have time to research what the standard says. I'll assume > lots of people can competently do that.
> I don't recall there having been an explicit request to change this, > so if in fact it was stronger under CLTL [note that CLTL2 is not in > the pedigree of ANSI CL--CLTL2 is a "rogue document", if you'll pardon > the colorful characterization] and no one can find a clean up issue > (there probably isn't one, but maybe I'm forgetting one), then you can > blame it on an editing error. That doesn't mean the error goes away, > but you at least know someone to frown at.
> Nonetheless, having already accepted the possible guilt for causing > the problem, I hope you'll now allow me to go ahead and argue that > this shouldn't be treated as a bug without criticizing me for trying > to defend my good name (now already sullied :).
> I will say that if I were an implementation, I would treat everything > to the left of a satisfies as something that must be done first, left- > to-right, for pragmatic reasons, even if the standard didn't require > it, as long as the standard doesn't forbid it (which I'm pretty sure > it doesn't). There are several reasons:
> (1) It's likely to be the case that the function call is more > expensive. The typechecks should be done first in a lot of cases, and > the ordering of the tests should presumably give you a cue of what is > least to most expensive to do. I think doing them in arbitrary order > isn't meaningful if you are not inlining everything, which is > presumably rare. You may be able to guess the speed of the > typechecks, but since the satisfies functions will be opaque, you've > got to trust the programmer.
Performance of the type test is not an issue. The compiler can reorder the type test to place the 'satisfies tests last in generated code, but the issue of breaking the commutative property still remains.
> (2) A great many functions useful as predicates are not total > functions and have type restrictions. oddp, plusp, etc. Those > functions become unuseful in this context if not allowed to be > ordered. If you do that, the usefulness of AND then becomes quite a > lot less. The primary idioms of use for AND in any combination with > SATISFIES are almost always to guard a predicate with appropriate type > checks, so to claim you can do a SATISFIES in any order is marginal. > (Note that nothing requires you to not mix tests where the user can't > tell. You can do (AND FOO BAR (SATISFIES BAZ)) in either order for FOO > and BAR because if the answer will be undetectably different it's just > a proper optimization... assuming FOO and BAR are not deftypes > expanding into other AND and SATISFIES, I mean. It's when a user- > visible effect happens that you mustn't shuffle things, and that's > where SATISFIES happens.
Good point about the existing predicates, but they can be encapsulated.
The compiler can make good use of other types in an intersection type along with a 'satisfies type. For example, with a '(and symbol (satisfies keywordp)) declaration the compiler can infer that a value is a 'symbol even if the 'satisfies test does not help and this is useful for optimization.
> (3) I totally dislike the notion that someone has some pet > optimization and that all language semantics have to bend to that > optimization. Admittedly, this isn't specified, so you can claim it's > not part of the semantics, but it will take you little effort to > realize that it was a meta-feature of the language design that we > consistently made the left-to-right decision rather than the arbitrary- > order decision. That's why we didn't make things non-deterministic in > class precedence lists, that's why we did floating point contagion as > we did, that's why the function call argument order evaluation is as > it is, that's why defsetf and a ton of related mechanism is as it > is, ... so why would we throw that away in this one case? > Optimizations should suit the language; languages should try not to be > merely bizarre syntaxes for asking for particular compiler > optimizations.
No one would want to bend the language to suit someones pet optimization or a particular compiler optimization.
Preserving the commutative property that exists among all other types hardly seems to fit.
I see merit in multiple points of view on this matter.
> (4) If there is a history of a previous interpretation and no evidence > of cleanup on the matter and there's a lot of code already deployed > that seems to interpret this a certain way, I think it's disruptive to > the normal state of affairs for a bunch of people with an alternate > interpretation that's clever but not evidenced as "relied upon" to > move in and say everyone else should rewrite their code just for > this. It's not been demonstrated that the relevant efficiency cannot > be achieved another way.
The 'satisfies type does not seem to be widely used. It is of limited use in declarations because it generally does not help code optimization.
Changes have been contributed to CL-HTTP.
No one is trying to impose a style on others. If there were strong preferences each way then I am sure a flag would be added to support both options.
> (5) If I could take a vote today, it would not be in support of > allowing arbitrary ordering, and that would have nothing to do with > legacy--it would have to do with the global design principle mentioned > in (3).
I see merit in both views, but the commutativity principle is so useful that I would prefer to see this prevail which can be met by the requirement that the 'satisfies predicate be written to accept any object.
> But that's just my personal opinion and has no official status. Your > mileage may vary. :)
> > So, it looks like 'we' will have to change the CL-HTTP code > > from (and symbol (satisfies fboundp)) > > to something like (and symbol (satisfies safe-fboundp)) > > as a type specifier.
> Well, admittedly that's safest. But it seems like giving up without a > fight. If you go that route, I think you're better just making safe- > fbound-symbol-p and using a single satisfies without the AND, so the > issue doesn't come up. Which is my whole point about AND. If every > use has to be written to assume it's not going to do this, that > reduces it to near uselessness. You're better off insisting that > implementations just do something reasonable.
The compiler can infer useful information from a '(and symbol (satisfies keywordp))) declaration that it can not from a 'fbound-symbol-p declaration.
On Dec 8, 12:53 pm, "Thomas F. Burdick" <tburd...@gmail.com> wrote:
> And it's a good thing that there isn't such a requirement, because it > would make a mess out of the type system. As it is, intersection types > can be simplified -- with a left-to-right ordering this could become > absurdly complex. Take for example:
> Do you really want to prohibit simplifying that to
> (and bit (satisfies evenp) ...)
I think your last statement misses a point fro the original post, which is that the order is irrelevant for the arguments of AND, except for SATISFIES. Hence, one may go for a partial ordering, such that type predicates (SATISFIES ...) come after everything else. This would allow both simplifications (you simplify the different non-satisfies types) and safety of the predicate (which can assume some type in the argument). I doubt making this change in CMUCL is as difficult as imposing a complete left-to-right order.
> On Dec 8, 12:53 pm, "Thomas F. Burdick" <tburd...@gmail.com> wrote:
> > And it's a good thing that there isn't such a requirement, because it > > would make a mess out of the type system. As it is, intersection types > > can be simplified -- with a left-to-right ordering this could become > > absurdly complex. Take for example:
> > Do you really want to prohibit simplifying that to
> > (and bit (satisfies evenp) ...)
> I think your last statement misses a point fro the original post, > which is that the order is irrelevant for the arguments of AND, except > for SATISFIES. Hence, one may go for a partial ordering, such that > type predicates (SATISFIES ...) come after everything else. This would > allow both simplifications (you simplify the different non-satisfies > types) and safety of the predicate (which can assume some type in the > argument). I doubt making this change in CMUCL is as difficult as > imposing a complete left-to-right order.
> Juanjo
Yes, a CL implementation can just give up on the 'satisfies type.
ANSI-CL: "subtypep is permitted to return the values false and false only when at least one argument involves one of these type specifiers: and, eql, the list form of function, member, not, or, satisfies, or value"
However a CL implementation is not required to give up when a type involves 'satisfies, and only in this context does the issue arise. CMU-CL does not give up and just ignores any errors from the predicate.
It would be very handy for the type system to be able to work with arbitrary subsets of intersection and union types rather than having to work around the 'satisfies type.
Consider type '(and a (satisfies b) c) which allows the subset '(and a (satisfies b)) to be compared with subtypep, but not the subset '(and (satisfies b) c).
Moving the 'satisfies type to the end does not resolve this, and the subset '(and c (satisfies b)) can not be compared.
Further the type '(and a c (satisfies b)) does not allow any subsets containing '(satisfies b) to be compared.
> On Dec 8, 12:53 pm, "Thomas F. Burdick" <tburd...@gmail.com> wrote:
> > And it's a good thing that there isn't such a requirement, because it > > would make a mess out of the type system. As it is, intersection types > > can be simplified -- with a left-to-right ordering this could become > > absurdly complex. Take for example:
> > Do you really want to prohibit simplifying that to
> > (and bit (satisfies evenp) ...)
> I think your last statement misses a point fro the original post, > which is that the order is irrelevant for the arguments of AND, except > for SATISFIES. Hence, one may go for a partial ordering, such that > type predicates (SATISFIES ...) come after everything else. This would > allow both simplifications (you simplify the different non-satisfies > types) and safety of the predicate (which can assume some type in the > argument). I doubt making this change in CMUCL is as difficult as > imposing a complete left-to-right order.
No, I did not miss the point of the post. Besides the excellent points from Douglas below, there's also the question of having a language with reasonable, simple semantics. The only two options that seem reasonable to me are for AND types to be strictly left-to-right, or to be pure intersection types. If you start going down the path you advocate above, you're going to end out with a complicated ad-hoc mess.
A reasonable alternative to left-to-right evaluation of AND types, that would still let Rainer do what he wants above, would have been to include a requirement that SATISFIES types ignore errors in their functions.
> On 8 déc, 14:49, Juanjo <juanjose.garciarip...@googlemail.com> wrote:
> > On Dec 8, 12:53 pm, "Thomas F. Burdick" <tburd...@gmail.com> wrote:
> > > And it's a good thing that there isn't such a requirement, because it > > > would make a mess out of the type system. As it is, intersection types > > > can be simplified -- with a left-to-right ordering this could become > > > absurdly complex. Take for example:
> > > Do you really want to prohibit simplifying that to
> > > (and bit (satisfies evenp) ...)
> > I think your last statement misses a point fro the original post, > > which is that the order is irrelevant for the arguments of AND, except > > for SATISFIES. Hence, one may go for a partial ordering, such that > > type predicates (SATISFIES ...) come after everything else. This would > > allow both simplifications (you simplify the different non-satisfies > > types) and safety of the predicate (which can assume some type in the > > argument). I doubt making this change in CMUCL is as difficult as > > imposing a complete left-to-right order.
> No, I did not miss the point of the post. Besides the excellent points > from Douglas below, there's also the question of having a language > with reasonable, simple semantics. The only two options that seem > reasonable to me are for AND types to be strictly left-to-right, or to > be pure intersection types. If you start going down the path you > advocate above, you're going to end out with a complicated ad-hoc > mess.
> A reasonable alternative to left-to-right evaluation of AND types, > that would still let Rainer do what he wants above, would have been to > include a requirement that SATISFIES types ignore errors in their > functions.
A left-right ordering on the 'and type and thus the 'or type would inhibit a lot of type inference capability and I expect would have objections from many.
Juanjo's proposal could be implemented by just giving up on the 'satisfies type without reworking the 'and type or imposing an ordering constraint. The type system would just not call the predicate and subtypep would just return (values nil nil). 'typep would still call the predicate, and Rainer's code would work as he expects. I see some merit in this too. If would also be easy to add a flag to control this and make it an option.
Depending on type errors being caught is not practical because they may not be signaled in un-safe code. A change to compilation policy, lowering the safety level, could break code badly, cause heap corruption, and this would occur unexpectedly because it was previously masked.