Re: [racket-dev] Proposal for a no-argument
On 07/01/2012 09:27 AM, Eli Barzilay wrote: There rare cases where it is useful to have a value that means that no argument was passed to a function. In many of these cases there is a plain value that is used as that mark, with the most idiomatic one being #f, but sometimes others are used. IMO, while such uses of #f are idiomatic, they're a hack where an argument's domain is extended only to mark no argument. I believe this is why CL lambda lists allow another variable name when specifying optional parameters. http://www.lispworks.com/documentation/HyperSpec/Body/03_dab.htm http://www.gigamonkeys.com/book/functions.html (defun f (optional (arg 'default-value arg-supplied?)) (if arg-supplied? 'real-value 'default-value)) Matlab's nargin and nargout are another interesting approach. http://www.mathworks.com/help/techdoc/ref/nargin.html http://www.mathworks.com/help/techdoc/ref/nargout.html A (supplied? arg) special form might also work. - Daniel _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Proposal for a no-argument
At Sun, 8 Jul 2012 08:40:41 -0400, Eli Barzilay wrote: Quick summary: I'll remove the #:before-first and #:after-last feature. If anyone wants them, please tell me -- maybe it can be left for the spliced case, or maybe they could always be spliced. +2 to always splicing. This gives us the additional features without needing the `#:nothing' argument. Vincent _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Proposal for a no-argument
Quick summary: I'll remove the #:before-first and #:after-last feature. If anyone wants them, please tell me -- maybe it can be left for the spliced case, or maybe they could always be spliced. On Monday, Matthew Flatt wrote: I'm not enthusiastic about this proposal. As you say at the start, it seems like a rare case. My main objection is that it's too rare to merit a change to the main `lambda' form and other parts of our infrastructure, such as documentation tools. This -- a more substantial extension of `lambda' and friends -- seems to be the core issue here. I've talked about it to Matthias, and we agreed on something that would help. I'll post about it separately, when I land. As a weaker objection, I don't agree with the suggestion that naive users can somehow ignore the `no-argument' value. I think it would show up prominently in documentation, contracts, and types. I didn't mean that they'll ignore it -- I meant that having a default expression that is rendered as no-argument or something similar is self explanatory in a better way than , and definitely from an additional #:nothing argument (which is an actual interface change in addition to being obscure). Weaker still, in terms of how well I can defend it: I'm suspicious of no-value values in general. Granted, we already have `#f', #void, and #undefined, but I'm not really happy about that (and I keep thinking about ways to get rid of #undefined). This could be solved, roughly, by what both Robby and Matthias suggested (protecting it from leaking out), but on one hand there's a problem with that, and on the other it makes it equivalent to exposing a boolean flag for the presence of an argument. That's the thing that I'll delay for a proper post on that. Side-question: do you have some potential direction for eliminating #undefined? (Asking because it might apply here too.) (Is there any problem with #void?) Meanwhile, the interface change, the gensym-encouraging, and the obscurer explanation really bother me, so I'll just get rid of the need for them by removing the #:before-first and #:after-last feature. -- ((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay: http://barzilay.org/ Maze is Life! _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Proposal for a no-argument
1. The need for a no-value initial value shows up only when we have a function with two (or more) such parameters. Otherwise case-lambda does fine. 2. Eli's initial proposal triggered the same response in me as Robby's except that our experience with 'undefined' immediately told me I want to trap variable accesses. In contrast to say touches on (real) futures or laziness, this kind of trap is way more expensive -- because variable accesses are so expensive. 3. BUT a local analysis of function bodies may suffice here. It would ensure that you check only immediately reachable variable occurrences and variable occurrences in tail positions. Otherwise (given? x) is blessed. 4. If this were reasonably cheap, it would solve our #f/#void/#undefined problem. 5. I know that this proposal looks so naive that someone else must have tried and measured it. Then again, perhaps it is too naive for anyone else to have tried and measured it. [We'd also need a proof that this is sound, but I consider my claim 'morally correct.'] -- Matthias _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Proposal for a no-argument
I really like this. Vincent At Sun, 1 Jul 2012 09:27:00 -0400, Eli Barzilay wrote: There rare cases where it is useful to have a value that means that no argument was passed to a function. In many of these cases there is a plain value that is used as that mark, with the most idiomatic one being #f, but sometimes others are used. IMO, while such uses of #f are idiomatic, they're a hack where an argument's domain is extended only to mark no argument. A more robust way to do that, which has become idiomatic in Racket is to use (gensym). (And as a sidenote, in other implementations there are various similar eq-based hacks.) IMO, this is an attempt to improve on the #f case by guaranteeing a unique value, but at its core it's still a similar hack. Recently, I have extended the `add-between' function in a way that ran against this problem at the interface level, where two keyword arguments default to such a gensymed value to detect when no argument is passed. Natually, this leaked into the documentation in the form of using `' to avoid specifying the default value and instead talking about what happens when no argument is given for the keywords in question. After a short discussion that I had with Matthew, the new version uses a new keyword that holds the unique no-value value, to simplify things: (define (foo x #:nothing [nothing (gensym)] [y nothing]) (printf y is ~s\n (if (eq? y nothing) 'omitted y))) The idea is that this does not depend on some specific unique value, since one can be given. For end-users of the function, there is no need to know about this. It's useful only for wrapper functions which want to mirror the same behavior, and call `foo' in a way that makes their own input passed to it, including not passing it when its own input is missing. In this case, you'd do something like this: (define (bar #:nothing [nothing (gensym)] [x nothing]) (foo 10 x #:nothing nothing)) This works, but I dislike this solution for several reasons: 1. Instead of finding a solution for the `gensym' problem, this approach embraces it as the proper way to do such things. 2. But more than that, it also exposes it in the interface of such functions, which means that simple end users need to read about it too. There is no easy way to somehow say you souldn't worry about this unless you're writing a function that ..., and if you look at the current docs for `add-between' you'd probably wonder when that #:nothing is useful. 3. There is also a half-story in this documentation -- even though the docs look like the above function definition, you obviously would want to define a single global gensymmed value and use it, to avoid redundant allocation. By the way the docs read, the above does look like the way to do these things, and I can see how a quick reading would make people believe that it's fine to write: (define (foo) (define (bar [x (gensym)]) ...) ... call bar many times ...) I considered a bunch of alternatives to this, and the one closest to looking reasonable is to use the #undefined value: it makes some sense because it is a value that is already used in some no value cases. However, it is probably a bad idea to use it for something different. In fact, that's how many languages end up with false, null, undefined, etc etc. (As a side note, a different approach would be to use a per-argument boolean flag that specifies if the corresponding argument. Since this started with a documentation point of view, I'm assuming that it won't be a good solution since it doesn't solve that problem -- a function that uses it similarly to `add-between' would still need to avoid specifying the default.) Instead, I suggest using a new special value, one that is used only for this purpose. The big difference from all of these special values is that I'm proposing a value that is used only for this. To discourage using it for other reasons, there would be no binding for it. Instead, there would be a fake one, say `no-argument', which is used only as a syntax in a default expression and only there the real no-argument gets used -- so the value is actually hidden and `no-argument' is a syntactic marker that is otherwise an error to use, like `else' and `='. (I'm no even suggesting making it a syntax parameter that has a value in default expressions, because you shouldn't be able to write (λ ([x (list no-argument)]) ...).) The only real binding that gets added is something that identifies that value, or even more convenient, something like `given?' that checks whether its input is *not* that value. To demonstrate how this looks like, assume that the kernel has only a three-argument `kernel-hash-ref', and you want to implement `hash-ref' on top of it without using a thunk (which avoid the problem in a different way). The
Re: [racket-dev] Proposal for a no-argument
On 2012-07-01 09:27:00 -0400, Eli Barzilay wrote: A more robust way to do that, which has become idiomatic in Racket is to use (gensym). (And as a sidenote, in other implementations there are various similar eq-based hacks.) IMO, this is an attempt to improve on the #f case by guaranteeing a unique value, but at its core it's still a similar hack. The gensym thing is used in parts of the GUI code for initialization arguments, e.g.: (class* mred% (area%) (init mk-wx get-wx-pan get-outer-wx-pan mismatches prnt [min-width no-val] [min-height no-val] [stretchable-width no-val] [stretchable-height no-val]) ...) Where `no-val` has been defined with a gensym. So it'd be nice to have the distinguished `no-argument` for these cases too. Cheers, Asumu _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Proposal for a no-argument
I'm not enthusiastic about this proposal. As you say at the start, it seems like a rare case. My main objection is that it's too rare to merit a change to the main `lambda' form and other parts of our infrastructure, such as documentation tools. As a weaker objection, I don't agree with the suggestion that naive users can somehow ignore the `no-argument' value. I think it would show up prominently in documentation, contracts, and types. Weaker still, in terms of how well I can defend it: I'm suspicious of no-value values in general. Granted, we already have `#f', #void, and #undefined, but I'm not really happy about that (and I keep thinking about ways to get rid of #undefined). At Sun, 1 Jul 2012 09:27:00 -0400, Eli Barzilay wrote: There rare cases where it is useful to have a value that means that no argument was passed to a function. In many of these cases there is a plain value that is used as that mark, with the most idiomatic one being #f, but sometimes others are used. IMO, while such uses of #f are idiomatic, they're a hack where an argument's domain is extended only to mark no argument. A more robust way to do that, which has become idiomatic in Racket is to use (gensym). (And as a sidenote, in other implementations there are various similar eq-based hacks.) IMO, this is an attempt to improve on the #f case by guaranteeing a unique value, but at its core it's still a similar hack. Recently, I have extended the `add-between' function in a way that ran against this problem at the interface level, where two keyword arguments default to such a gensymed value to detect when no argument is passed. Natually, this leaked into the documentation in the form of using `' to avoid specifying the default value and instead talking about what happens when no argument is given for the keywords in question. After a short discussion that I had with Matthew, the new version uses a new keyword that holds the unique no-value value, to simplify things: (define (foo x #:nothing [nothing (gensym)] [y nothing]) (printf y is ~s\n (if (eq? y nothing) 'omitted y))) The idea is that this does not depend on some specific unique value, since one can be given. For end-users of the function, there is no need to know about this. It's useful only for wrapper functions which want to mirror the same behavior, and call `foo' in a way that makes their own input passed to it, including not passing it when its own input is missing. In this case, you'd do something like this: (define (bar #:nothing [nothing (gensym)] [x nothing]) (foo 10 x #:nothing nothing)) This works, but I dislike this solution for several reasons: 1. Instead of finding a solution for the `gensym' problem, this approach embraces it as the proper way to do such things. 2. But more than that, it also exposes it in the interface of such functions, which means that simple end users need to read about it too. There is no easy way to somehow say you souldn't worry about this unless you're writing a function that ..., and if you look at the current docs for `add-between' you'd probably wonder when that #:nothing is useful. 3. There is also a half-story in this documentation -- even though the docs look like the above function definition, you obviously would want to define a single global gensymmed value and use it, to avoid redundant allocation. By the way the docs read, the above does look like the way to do these things, and I can see how a quick reading would make people believe that it's fine to write: (define (foo) (define (bar [x (gensym)]) ...) ... call bar many times ...) I considered a bunch of alternatives to this, and the one closest to looking reasonable is to use the #undefined value: it makes some sense because it is a value that is already used in some no value cases. However, it is probably a bad idea to use it for something different. In fact, that's how many languages end up with false, null, undefined, etc etc. (As a side note, a different approach would be to use a per-argument boolean flag that specifies if the corresponding argument. Since this started with a documentation point of view, I'm assuming that it won't be a good solution since it doesn't solve that problem -- a function that uses it similarly to `add-between' would still need to avoid specifying the default.) Instead, I suggest using a new special value, one that is used only for this purpose. The big difference from all of these special values is that I'm proposing a value that is used only for this. To discourage using it for other reasons, there would be no binding for it. Instead, there would be a fake one, say `no-argument', which is used only as a syntax in a default expression and only there the real no-argument gets used -- so the value is actually hidden and `no-argument' is a
Re: [racket-dev] Proposal for a no-argument
At Mon, 2 Jul 2012 12:14:02 -0400, Asumu Takikawa wrote: The gensym thing is used in parts of the GUI code for initialization arguments, e.g.: (class* mred% (area%) (init mk-wx get-wx-pan get-outer-wx-pan mismatches prnt [min-width no-val] [min-height no-val] [stretchable-width no-val] [stretchable-height no-val]) ...) Where `no-val` has been defined with a gensym. So it'd be nice to have the distinguished `no-argument` for these cases too. In those cases, `no-val' is just an internal implementation technique, and not intended as a part of the API. In the case of `min-height' and `min-width', I think it would be better to allow `#f' as an argument, since the documented default value is not really accessible. _ Racket Developers list: http://lists.racket-lang.org/dev
Re: [racket-dev] Proposal for a no-argument
May be the discussion goes beyond my understanding, in which case sorry for my noise. Where I need a value distinct from all other values (such as a no-value), I prepare an empty struct type and export one single instance of this struct together with its predicate (and nothing else) Jos _ Racket Developers list: http://lists.racket-lang.org/dev
[racket-dev] Proposal for a no-argument
There rare cases where it is useful to have a value that means that no argument was passed to a function. In many of these cases there is a plain value that is used as that mark, with the most idiomatic one being #f, but sometimes others are used. IMO, while such uses of #f are idiomatic, they're a hack where an argument's domain is extended only to mark no argument. A more robust way to do that, which has become idiomatic in Racket is to use (gensym). (And as a sidenote, in other implementations there are various similar eq-based hacks.) IMO, this is an attempt to improve on the #f case by guaranteeing a unique value, but at its core it's still a similar hack. Recently, I have extended the `add-between' function in a way that ran against this problem at the interface level, where two keyword arguments default to such a gensymed value to detect when no argument is passed. Natually, this leaked into the documentation in the form of using `' to avoid specifying the default value and instead talking about what happens when no argument is given for the keywords in question. After a short discussion that I had with Matthew, the new version uses a new keyword that holds the unique no-value value, to simplify things: (define (foo x #:nothing [nothing (gensym)] [y nothing]) (printf y is ~s\n (if (eq? y nothing) 'omitted y))) The idea is that this does not depend on some specific unique value, since one can be given. For end-users of the function, there is no need to know about this. It's useful only for wrapper functions which want to mirror the same behavior, and call `foo' in a way that makes their own input passed to it, including not passing it when its own input is missing. In this case, you'd do something like this: (define (bar #:nothing [nothing (gensym)] [x nothing]) (foo 10 x #:nothing nothing)) This works, but I dislike this solution for several reasons: 1. Instead of finding a solution for the `gensym' problem, this approach embraces it as the proper way to do such things. 2. But more than that, it also exposes it in the interface of such functions, which means that simple end users need to read about it too. There is no easy way to somehow say you souldn't worry about this unless you're writing a function that ..., and if you look at the current docs for `add-between' you'd probably wonder when that #:nothing is useful. 3. There is also a half-story in this documentation -- even though the docs look like the above function definition, you obviously would want to define a single global gensymmed value and use it, to avoid redundant allocation. By the way the docs read, the above does look like the way to do these things, and I can see how a quick reading would make people believe that it's fine to write: (define (foo) (define (bar [x (gensym)]) ...) ... call bar many times ...) I considered a bunch of alternatives to this, and the one closest to looking reasonable is to use the #undefined value: it makes some sense because it is a value that is already used in some no value cases. However, it is probably a bad idea to use it for something different. In fact, that's how many languages end up with false, null, undefined, etc etc. (As a side note, a different approach would be to use a per-argument boolean flag that specifies if the corresponding argument. Since this started with a documentation point of view, I'm assuming that it won't be a good solution since it doesn't solve that problem -- a function that uses it similarly to `add-between' would still need to avoid specifying the default.) Instead, I suggest using a new special value, one that is used only for this purpose. The big difference from all of these special values is that I'm proposing a value that is used only for this. To discourage using it for other reasons, there would be no binding for it. Instead, there would be a fake one, say `no-argument', which is used only as a syntax in a default expression and only there the real no-argument gets used -- so the value is actually hidden and `no-argument' is a syntactic marker that is otherwise an error to use, like `else' and `='. (I'm no even suggesting making it a syntax parameter that has a value in default expressions, because you shouldn't be able to write (λ ([x (list no-argument)]) ...).) The only real binding that gets added is something that identifies that value, or even more convenient, something like `given?' that checks whether its input is *not* that value. To demonstrate how this looks like, assume that the kernel has only a three-argument `kernel-hash-ref', and you want to implement `hash-ref' on top of it without using a thunk (which avoid the problem in a different way). The so-far-idiomatic code could be as follows: (define none (gensym)) ; private binding (define (hash-ref t k [d none]) (cond [(not (eq? d none)) (kernel-hash-ref t k d)]
Re: [racket-dev] Proposal for a no-argument
If you're only going to use in keyword arguments (and optional arguments), you could make it an error to touch the value, unless it gets touched by a special predicate that checks for its existence. That is, in (define (f #:x [x]) ...) (where I'm saying that leaving off the default value means it gets this new, special behavior), you'd bind, at compile time, 'x' to something that expands to a code that signals error and (is-default? x) would look at its argument and be able to actually implement the predicate. In other words, I think you can devise a scheme where you don't actually have a value that leaks out at all. Robby On Sun, Jul 1, 2012 at 8:27 AM, Eli Barzilay e...@barzilay.org wrote: There rare cases where it is useful to have a value that means that no argument was passed to a function. In many of these cases there is a plain value that is used as that mark, with the most idiomatic one being #f, but sometimes others are used. IMO, while such uses of #f are idiomatic, they're a hack where an argument's domain is extended only to mark no argument. A more robust way to do that, which has become idiomatic in Racket is to use (gensym). (And as a sidenote, in other implementations there are various similar eq-based hacks.) IMO, this is an attempt to improve on the #f case by guaranteeing a unique value, but at its core it's still a similar hack. Recently, I have extended the `add-between' function in a way that ran against this problem at the interface level, where two keyword arguments default to such a gensymed value to detect when no argument is passed. Natually, this leaked into the documentation in the form of using `' to avoid specifying the default value and instead talking about what happens when no argument is given for the keywords in question. After a short discussion that I had with Matthew, the new version uses a new keyword that holds the unique no-value value, to simplify things: (define (foo x #:nothing [nothing (gensym)] [y nothing]) (printf y is ~s\n (if (eq? y nothing) 'omitted y))) The idea is that this does not depend on some specific unique value, since one can be given. For end-users of the function, there is no need to know about this. It's useful only for wrapper functions which want to mirror the same behavior, and call `foo' in a way that makes their own input passed to it, including not passing it when its own input is missing. In this case, you'd do something like this: (define (bar #:nothing [nothing (gensym)] [x nothing]) (foo 10 x #:nothing nothing)) This works, but I dislike this solution for several reasons: 1. Instead of finding a solution for the `gensym' problem, this approach embraces it as the proper way to do such things. 2. But more than that, it also exposes it in the interface of such functions, which means that simple end users need to read about it too. There is no easy way to somehow say you souldn't worry about this unless you're writing a function that ..., and if you look at the current docs for `add-between' you'd probably wonder when that #:nothing is useful. 3. There is also a half-story in this documentation -- even though the docs look like the above function definition, you obviously would want to define a single global gensymmed value and use it, to avoid redundant allocation. By the way the docs read, the above does look like the way to do these things, and I can see how a quick reading would make people believe that it's fine to write: (define (foo) (define (bar [x (gensym)]) ...) ... call bar many times ...) I considered a bunch of alternatives to this, and the one closest to looking reasonable is to use the #undefined value: it makes some sense because it is a value that is already used in some no value cases. However, it is probably a bad idea to use it for something different. In fact, that's how many languages end up with false, null, undefined, etc etc. (As a side note, a different approach would be to use a per-argument boolean flag that specifies if the corresponding argument. Since this started with a documentation point of view, I'm assuming that it won't be a good solution since it doesn't solve that problem -- a function that uses it similarly to `add-between' would still need to avoid specifying the default.) Instead, I suggest using a new special value, one that is used only for this purpose. The big difference from all of these special values is that I'm proposing a value that is used only for this. To discourage using it for other reasons, there would be no binding for it. Instead, there would be a fake one, say `no-argument', which is used only as a syntax in a default expression and only there the real no-argument gets used -- so the value is actually hidden and `no-argument' is a syntactic marker that is otherwise an error to use, like
Re: [racket-dev] Proposal for a no-argument
Just now, Robby Findler wrote: If you're only going to use in keyword arguments (and optional arguments), you could make it an error to touch the value, unless it gets touched by a special predicate that checks for its existence. That is, in (define (f #:x [x]) ...) (where I'm saying that leaving off the default value means it gets this new, special behavior), (Just to clarify, I mentioned this option for completeness, but I think that having a keyword will make things much simpler for macros etc.) you'd bind, at compile time, 'x' to something that expands to a code that signals error and (is-default? x) would look at its argument and be able to actually implement the predicate. In other words, I think you can devise a scheme where you don't actually have a value that leaks out at all. Yeah, something like that could be done -- but there is a case where you should be able to use `x' as a valid expression withouch checking, when you wrap a function. I should have added this to the examples to show that case too. Here's such an example for some `get-thing' function that uses the `get-hash' from the previous message. First, with the current idiom: (define none (gensym)) ; need my own none value (define (get-thing name [default none]) (if (eq? default none) (hash-ref things name) (hash-ref things name default))) Second, with the #:nothing thing this is a little easier: (define nothing (gensym)) ; still need my own (define (get-thing name #:nothing [nothing nothing] [default nothing]) (hash-ref things name default #:nothing nothing)) And using my proposal, it's much easier and there's no need for a new nothing value since there's a single one: (define (get-thing name [default no-argument]) (hash-ref things name default)) And that shows a use of `default' that would be valid even though it's not tested. -- ((lambda (x) (x x)) (lambda (x) (x x))) Eli Barzilay: http://barzilay.org/ Maze is Life! _ Racket Developers list: http://lists.racket-lang.org/dev