digitalmars.D - Design of intuitive interfaces
- Norbert Nemec <Norbert Nemec-online.de> Feb 20 2010
- Michel Fortin <michel.fortin michelf.com> Feb 21 2010
- bearophile <bearophileHUGS lycos.com> Feb 21 2010
- Michel Fortin <michel.fortin michelf.com> Feb 21 2010
- bearophile <bearophileHUGS lycos.com> Feb 21 2010
- Ary Borenszweig <ary esperanto.org.ar> Feb 21 2010
- KennyTM~ <kennytm gmail.com> Feb 21 2010
- Ary Borenszweig <ary esperanto.org.ar> Feb 21 2010
- KennyTM~ <kennytm gmail.com> Feb 22 2010
- Jacob Carlborg <doob me.com> Feb 21 2010
- "Nick Sabalausky" <a a.a> Feb 21 2010
- Jacob Carlborg <doob me.com> Feb 21 2010
- Bill Baxter <wbaxter gmail.com> Feb 21 2010
- "Adam D. Ruppe" <destructionator gmail.com> Feb 21 2010
- Walter Bright <newshound1 digitalmars.com> Feb 21 2010
- "Steven Schveighoffer" <schveiguy yahoo.com> Feb 22 2010
- "Steven Schveighoffer" <schveiguy yahoo.com> Feb 22 2010
Hi there, lately, there was some heated discussion about the naming of functions in the standard libraries. Rather than chiming in, I'd like to share some very general thoughts about this. Writing *intuitive* interfaces, be it for libraries or applications, is a very different issue than writing *correct* or *efficient* software. The most important difference is, how to react to to user feedback. In terms of correctness or efficiency, each single feedback is either right or wrong, so you can easily argue about its validity. Feedback about the intuitiveness of an interface, on the other hand, is by definition always right. After all it is a message from a user: "I had the following experience." Of course, there is still plenty of room to discuss it (wouldn't it be boring if there was nothing to talk about?) but it cannot be argued away. For one person speaking up, there are often many others experiencing the same thing and keeping silent. As for function naming: intuitive names are an essential aspect of the quality of a library. Of course, one could use a Russian library without understanding any of the names, but it would be a painful experience. Good function names should give a rough idea of their meaning and, more important, they should me memorable after reading the docs once. Commercial software design spends lots of time and money on field studies, doing statistics (yes, statistics!) on user feedback. Not having these resources, we have to make assumptions on our target user's background. Every interface has to be designed towards an audience. If it fails to address this audience, it will fail. Trying to imagine a "typical" user of the D library, I see a 21st century person with reasonable knowledge of the English language and some background other mainstream programming languages. Seeing the word "retro" that person will think "old style" rather than picking up the dictionary and looking up the original meaning. Seeing "iota", the person will think "???". No matter how good the explanation for any library name is, the fact that you have to give the explanation indicates that it is not intuitive. Now, of course, intuitivity is not everything. Conciseness is very important. Sometimes, introducing a new word is a good solution. Think of it as introducing a new technical term in writing an article or a book. Introducing a term that you cannot assume to be known by the audience, you first have to give an explanation. Doing so, you have to consider: is it worth introducing this term? Is it really more concise than using something known? OK, now, before this whole post begins to sound constipated, I'd rather finish by giving my own user feedback about the function names that were discussed. The name "retro" feels awkward to me as well. As this is a matter of in-place vs. input-preserving, I would suggest the solution in Python/NumPy: "sort" gives the command to sort data in-place "sorted" returns a sorted data, preserving the input similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy The name "iota" seems confusing to me as well. Very few greek letters do indeed have a conventional meaning (e.g. lambda-calculus, delta for a difference or epsilon for a really small value). Calling a function by a meaningless name is not a good idea. Don't have a better idea right now, though... But as I said, usability really is about the "average" member of the audience, so this is really just one voice out of many. Greetings, Norbert
Feb 20 2010
On 2010-02-21 02:15:23 -0500, Norbert Nemec <Norbert Nemec-online.de> said:I would suggest the solution in Python/NumPy: "sort" gives the command to sort data in-place "sorted" returns a sorted data, preserving the input similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy
I that's a not so bad solution, applicable to almost any word. There are cases where it doesn't work ('split'), but probably not too much. The remaining problem with this is that it can easily be confused with a boolean property. Does "array.sorted" return true or false depending on whether the array is sorted, or does it return a sorted array? Perhaps this is where a property would be useful: array.sort() // sort in place array.sorted() // create sorted copy array.sorted // tell if the array is sorted but it doesn't scale when you need an argument. So I suggest that functions for evaluating a boolean characteristic start with the "is" prefix: array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate I updated the D Programming Guidelines I wrote a while ago on Wiki4D to match this. <http://www.wikiservice.at/d/wiki.cgi?DProgrammingGuidelines>The name "iota" seems confusing to me as well. Very few greek letters do indeed have a conventional meaning (e.g. lambda-calculus, delta for a difference or epsilon for a really small value). Calling a function by a meaningless name is not a good idea. Don't have a better idea right now, though...
Perhaps "interval"? After all, iota(1, 10) is the same concept as 1..10 which denotes an interval. Ideally we could reuse the ".." operator to create an interval, but it seems we can't. The next good solution is to use the same terminology. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 21 2010
Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate) Bye, bearophile
Feb 21 2010
On 2010-02-21 10:19:06 -0500, bearophile <bearophileHUGS lycos.com> said:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate)
Note that Ruby only accept this as a suffix, but yeah it's part of the identifier. And I'd love this, but the ! suffix is totally ambiguous with the template instantiation syntax, and the ? suffix would be ambiguous in the ternary operator "?:". -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Feb 21 2010
Michel Fortin:the ! suffix is totally ambiguous with the template instantiation syntax, and the ? suffix would be ambiguous in the ternary operator "?:".
OK, sorry for saying silly things again :-) I sometimes have used an upper case Q for the ? (it stands for Question mark): sortedQ() Bye, bearophile
Feb 21 2010
Michel Fortin wrote:On 2010-02-21 10:19:06 -0500, bearophile <bearophileHUGS lycos.com> said:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate)
Note that Ruby only accept this as a suffix, but yeah it's part of the identifier. And I'd love this, but the ! suffix is totally ambiguous with the template instantiation syntax, and the ? suffix would be ambiguous in the ternary operator "?:".
But ruby has the ternary operator "?:", it just gives the "?" in the identifier more precedence. So there's just the backwards compatibility problem, but if you had: foo? something : something_else then now it won't compile ("foo?" can't be found) and you'll have to change it to foo ? something : something_else so it's a safe backwards incompatible change.
Feb 21 2010
On Feb 22, 10 06:07, Ary Borenszweig wrote:Michel Fortin wrote:On 2010-02-21 10:19:06 -0500, bearophile <bearophileHUGS lycos.com> said:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate)
Note that Ruby only accept this as a suffix, but yeah it's part of the identifier. And I'd love this, but the ! suffix is totally ambiguous with the template instantiation syntax, and the ? suffix would be ambiguous in the ternary operator "?:".
But ruby has the ternary operator "?:", it just gives the "?" in the identifier more precedence. So there's just the backwards compatibility problem, but if you had: foo? something : something_else then now it won't compile ("foo?" can't be found) and you'll have to change it to foo ? something : something_else so it's a safe backwards incompatible change.
enum foo = false; enum bar = 2; ... case foo?(1):bar: break; Suddenly, if "foo?" is defined, the value of the case completely changed and you've got a "bar" label.
Feb 21 2010
KennyTM~ wrote:On Feb 22, 10 06:07, Ary Borenszweig wrote:Michel Fortin wrote:On 2010-02-21 10:19:06 -0500, bearophile <bearophileHUGS lycos.com> said:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate)
Note that Ruby only accept this as a suffix, but yeah it's part of the identifier. And I'd love this, but the ! suffix is totally ambiguous with the template instantiation syntax, and the ? suffix would be ambiguous in the ternary operator "?:".
But ruby has the ternary operator "?:", it just gives the "?" in the identifier more precedence. So there's just the backwards compatibility problem, but if you had: foo? something : something_else then now it won't compile ("foo?" can't be found) and you'll have to change it to foo ? something : something_else so it's a safe backwards incompatible change.
enum foo = false; enum bar = 2; ... case foo?(1):bar: break; Suddenly, if "foo?" is defined, the value of the case completely changed and you've got a "bar" label.
Cases are known at compile-time.
Feb 21 2010
On Feb 22, 10 07:29, Ary Borenszweig wrote:KennyTM~ wrote:On Feb 22, 10 06:07, Ary Borenszweig wrote:Michel Fortin wrote:On 2010-02-21 10:19:06 -0500, bearophile <bearophileHUGS lycos.com> said:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate)
Note that Ruby only accept this as a suffix, but yeah it's part of the identifier. And I'd love this, but the ! suffix is totally ambiguous with the template instantiation syntax, and the ? suffix would be ambiguous in the ternary operator "?:".
But ruby has the ternary operator "?:", it just gives the "?" in the identifier more precedence. So there's just the backwards compatibility problem, but if you had: foo? something : something_else then now it won't compile ("foo?" can't be found) and you'll have to change it to foo ? something : something_else so it's a safe backwards incompatible change.
enum foo = false; enum bar = 2; ... case foo?(1):bar: break; Suddenly, if "foo?" is defined, the value of the case completely changed and you've got a "bar" label.
Cases are known at compile-time.
The program is changed when you recompile. (The foo? function can be CTFE). The point is it is an unsafe backward incompatible change.
Feb 22 2010
On 2/21/10 16:19, bearophile wrote:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate) Bye, bearophile
I never liked that with ruby, I would prefer Michel Fortin's suggestion.
Feb 21 2010
"Jacob Carlborg" <doob me.com> wrote in message news:hlrk2c$2kq7$2 digitalmars.com...On 2/21/10 16:19, bearophile wrote:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate) Bye, bearophile
I never liked that with ruby, I would prefer Michel Fortin's suggestion.
I'm surprised to hear that. I always thought it was very clean and more generally-useful than things like isBlah (I've frequently run into cases where the isBlah couldn't be used as it would have resulted in a gibberish variable name). Only problem I ever saw with it is that it's not a realistic possibility in D due to ambiguities with other parts of D's syntax.
Feb 21 2010
On 2010-02-21 21.06, Nick Sabalausky wrote:"Jacob Carlborg"<doob me.com> wrote in message news:hlrk2c$2kq7$2 digitalmars.com...On 2/21/10 16:19, bearophile wrote:Michel Fortin:array.sort(predicate) // sort in place using predicate array.sorted(predicate) // create sorted copy using predicate array.isSorted(predicate) // tell if the array is sorted using predicate
Good. Another possibility is to let D2 accept ? and ! too inside variable names, so they can become (as in Ruby I think, and something similar is common in Lisp-like languages too): array.sort(predicate) array.sort!(predicate); // void function array.sorted?(predicate) Bye, bearophile
I never liked that with ruby, I would prefer Michel Fortin's suggestion.
I'm surprised to hear that. I always thought it was very clean and more generally-useful than things like isBlah (I've frequently run into cases where the isBlah couldn't be used as it would have resulted in a gibberish variable name). Only problem I ever saw with it is that it's not a realistic possibility in D due to ambiguities with other parts of D's syntax.
Maybe it's because typing a ? or ! doesn't fit as good in my hands compared to other characters, this is using a swedish which requires two keys to be pressed. I think using the "is" prefix makes it more like english if you read it, for example, I read "if(array.isSorted)" as "if array is sorted".
Feb 21 2010
On Sun, Feb 21, 2010 at 7:36 AM, bearophile <bearophileHUGS lycos.com> wrote:I sometimes have used an upper case Q for the ? (it stands for Question mark): sortedQ()
I would think that was a shorthand for some kind of "sorted queue" thing if I ran into it in the wild. --bb
Feb 21 2010
On Sun, Feb 21, 2010 at 10:36:53AM -0500, bearophile wrote:I sometimes have used an upper case Q for the ? (it stands for Question mark): sortedQ()
Or the lisp convention of -p? sorted_p() Bah, I'd just say go with isSorted. sorted? is just bizarre anyway. -- Adam D. Ruppe http://arsdnet.net
Feb 21 2010
Michel Fortin wrote:On 2010-02-21 02:15:23 -0500, Norbert Nemec <Norbert Nemec-online.de> said:similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy
I that's a not so bad solution, applicable to almost any word. There are cases where it doesn't work ('split'), but probably not too much.
"split" - to split in place "splat" - to return a modified copy <g>
Feb 21 2010
Walter Bright wrote:Michel Fortin wrote:On 2010-02-21 02:15:23 -0500, Norbert Nemec <Norbert Nemec-online.de> said:similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy
I that's a not so bad solution, applicable to almost any word. There are cases where it doesn't work ('split'), but probably not too much.
"split" - to split in place "splat" - to return a modified copy
Just to clarify: there is some point being missed here. It's not about in-place vs. copy. Please check retro's documentation. Andrei
Feb 21 2010
Steven Schveighoffer wrote:On Sun, 21 Feb 2010 16:21:29 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Walter Bright wrote:Michel Fortin wrote:On 2010-02-21 02:15:23 -0500, Norbert Nemec <Norbert Nemec-online.de> said:similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy
I that's a not so bad solution, applicable to almost any word. There are cases where it doesn't work ('split'), but probably not too much.
"splat" - to return a modified copy
Just to clarify: there is some point being missed here. It's not about in-place vs. copy. Please check retro's documentation.
By copy he means it doesn't affect the original. Retro returns a "virtual" copy :)
But it's a valid point. There are three degrees of freedom here, which ideally should be reflected in the API: in-place, copy and view (or virtual copy if you like). -Lars
Feb 22 2010
On Sun, 21 Feb 2010 16:21:29 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Walter Bright wrote:Michel Fortin wrote:On 2010-02-21 02:15:23 -0500, Norbert Nemec <Norbert Nemec-online.de> said:similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy
I that's a not so bad solution, applicable to almost any word. There are cases where it doesn't work ('split'), but probably not too much.
"splat" - to return a modified copy
Just to clarify: there is some point being missed here. It's not about in-place vs. copy. Please check retro's documentation.
By copy he means it doesn't affect the original. Retro returns a "virtual" copy :) -Steve
Feb 22 2010
On Mon, 22 Feb 2010 08:24:18 -0500, Lars T. Kyllingstad <public kyllingen.nospamnet> wrote:Steven Schveighoffer wrote:On Sun, 21 Feb 2010 16:21:29 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Walter Bright wrote:Michel Fortin wrote:On 2010-02-21 02:15:23 -0500, Norbert Nemec <Norbert Nemec-online.de> said:similarly, I would suggest "reverse" to sort in-place "reversed" to return a modified copy
I that's a not so bad solution, applicable to almost any word. There are cases where it doesn't work ('split'), but probably not too much.
"splat" - to return a modified copy
Just to clarify: there is some point being missed here. It's not about in-place vs. copy. Please check retro's documentation.
"virtual" copy :)
But it's a valid point. There are three degrees of freedom here, which ideally should be reflected in the API: in-place, copy and view (or virtual copy if you like).
As far as English terms go, how do you distinguish between a lazy and eager copy? There are actually even more subtle possibilities. For example, split could return an array of slices -- which is not a copy of the data, but is a unique copy of the split points. I think it's not possible to have a rule to encompass all possibilities, but a rule to cover 80% of the most useful representations is good enough. Also, I would think one would prefer a view whenever possible, because you can always make a hard copy from the view, but you can't do it the other way around. -Steve
Feb 22 2010









bearophile <bearophileHUGS lycos.com> 