www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Design of intuitive interfaces

reply Norbert Nemec <Norbert Nemec-online.de> writes:
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
parent reply Michel Fortin <michel.fortin michelf.com> writes:
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
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
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
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
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
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
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
prev sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
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
parent reply KennyTM~ <kennytm gmail.com> writes:
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
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
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
parent KennyTM~ <kennytm gmail.com> writes:
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
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
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
parent reply "Nick Sabalausky" <a a.a> writes:
"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
parent Jacob Carlborg <doob me.com> writes:
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
prev sibling next sibling parent Bill Baxter <wbaxter gmail.com> writes:
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
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
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
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
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
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
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