AdaCore / Ada_Drivers_Library

Ada source code and complete sample GNAT projects for selected bare-board platforms supported by GNAT.
BSD 3-Clause "New" or "Revised" License
240 stars 141 forks source link

Micro:Bit add time, ADC and buttons, fix TWI (I2C) #144

Closed Fabien-Chouteau closed 7 years ago

Fabien-Chouteau commented 7 years ago

Those function names are good procedure names. ;-)

I don't get it. I didn't use Get_* :)

pat-rogers commented 7 years ago

On 2/14/2017 4:35 AM, Fabien Chouteau wrote:

Those function names are good procedure names. ;-)

I don't get it. I didn't use Get_* :)

True, and much appreciated. This is directly related though.

[begin preaching]

Because Ada distinguishes functions from procedures, the naming scheme for them can be different, to the benefit of the reader. At the language level, procedures can never be used as functions, and vice versa. (Yes, functions can go do things as well as return a value, but they can never be called like a procedure, ie in a statement context.)

Procedures always represent a set of actions that do something, whereas functions always return (and thus represent) values. It is true that functions can perform some set of actions too, but should they? Rarely, but sometimes yes. That amounts to side effects, a possibility with functions that can lead to nasty issues, and so are to be avoided when possible. "Memo functions" and such, eg random number generator functions, have side effects by definition, but those effects are benign.

So procedure names are ideally actions, ie imperative verbs that mean "go do this thing", but function names are ideally either predicate names (for boolean functions) or nouns. Nobody would find the name Compute_Sine to be a good name for the mathematical Sine function, for example. We just want to say "Sin (X)" in an expression.

This is not just my opinion. It is a very common one and it reflects the original language design. That's one reason why it is the scheme specified in the Ada Quality and Style Guidebook. See section 3.2.5 "Program Unit Names" in the document, available here: http://www.adaic.org/resources/add_content/docs/95style/95style.pdf

My personal scheme is slightly different, in that I find the "Is_" prefix for predicate names to be redundant, but that is a minor point. I've used both.

All of this is about readability, critical to so many other desirable qualities, such as reliability, which is why I harp about it all the time. I spend a lot of time refactoring names to represent the domain space and for readability, because I think it is that important.

A naming scheme is a way of passing more information to the reader, similar to the many other things we tell the reader by our choices in the code. For example, if we declare something (eg a variable) at the level of a package body, we are telling the reader, because of the reader's knowledge of the visibility rules, that this entity is used by multiple units within the package body. Such use is not required by the language, it could certainly be used by only one of them, but if so that would be a misleading choice of placement. We'd be forcing the reader to recognize that the usage is not global to the package body after all, something they would rightfully assume on first encountering it.

[end preaching]

So, to give a concrete example, the name "Read_*" would be a procedure name because "read" is an imperative verb.

I don't make a big deal of many things (I hope!), but readability is critical to so many other desirable qualities that I consider it very important.

Fabien-Chouteau commented 7 years ago

So, to give a concrete example, the name "Read_*" would be a procedure name because "read" is an imperative verb.

I think I agree with that, actually can you write a short version of your preach in design.md or a new file style.md so that we can capture it. I don't know if we will have means to automatically enforce this kind of naming scheme.

In this very instance (Microbit.IO) I'm reusing the naming of the main MicroBit library (in python), so I'm sort of following the domain. I can at least document this choice.

pat-rogers commented 7 years ago

On 2/20/2017 9:26 AM, Fabien Chouteau wrote:

So, to give a concrete example, the name "Read_*" would be a procedure name because "read" is an imperative verb.

I think I agree with that, actually can you write a short version of your preach in design.md or a new file style.md so that we can capture it. I don't know if we will have means to automatically enforce this kind of naming scheme.

I'll be happy to write it up. You're right, though, I don't think it is automatically enforceable, we'll have to rely on manual reviews.

In this very instance (Microbit.IO) I'm reusing the naming of the main MicroBit library (in python), so I'm sort of following the domain. I can at least document this choice.

Who will read it more, Ada users or Python users? :-) :-)

Fabien-Chouteau commented 7 years ago

I'll be happy to write it up. You're right, though, I don't think it is automatically enforceable, we'll have to rely on manual reviews.

Thanks!

Who will read it more, Ada users or Python users? :-) :-)

Frankly, Python users I hope :)

pat-rogers commented 7 years ago

On 2/20/2017 4:27 PM, Fabien Chouteau wrote:

I'll be happy to write it up. You're right, though, I don't think it is automatically enforceable, we'll have to rely on manual reviews.

Thanks!

Who will read it more, Ada users or Python users? :-) :-)

Frankly, Python users I hope :)

In half-serious mode: you could get fancy and introduce a renaming for each routine, introducing the Python name. Or just a comment. :-)