Doubts in ada language involving procedures - ada

I am a beginner in Ada and I have come across a piece of code which is shown below:
procedure Null_Proc is
begin
null;
end;
Now as per my knowledge the procedure in Ada doesn't return anything. My doubt is what does this procedure Null_proc do? I mean I am not clear with the definition of the procedure.

It does nothing.
It might be useful when a procedure must be called but nothing must be done; otherwise, it has little value. (I am working from memory; I assume that Ada does allow functions or procedures as parameters to other functions - in terms of C, pointers to functions.)

I've been known to write main routines that way when all the "real code" was in the withed packages. This is particularly likely if your program uses tasking, as the main routine cannot accept rendezvous like a task can, so it often ends up with nothing useful to do. Your entire program will stay active until all tasks complete, so the main routine really doesn't have to do anything.
Another possible use would be for implementing some kind of default routine to supply to callbacks.

Related

Avoid copying objects when passing records as function parameters in Ada

When passing a in record to a function or procedure in Ada, it's passed by value.
Should I then pass big records as access constant to avoid the copy (as you would do in C++)?
Are Ada compilers usually able to optimize that copy automatically?
No! The compiler is free to choose by-copy or by-reference. See the Language Reference Manual
No!
You could say so. The compiler is free to choose what it finds best, and it is my experience that compilers "know" how to generate code better than almost all software developers.

Binding vs Linking in Ada

I wonder what is the fundamental difference between binding and linking when working with Ada code? I couldn't find a good explanation on google and this is why I ask the question.
For the binding process what is the input and what is the output?
What is the relation between binding and linking? I assume binding needs to be done first.
Thanks,
Bogdan.
With GNAT, there are two jobs which the binder performs: first, checking that all the necessary compilations have been done, so that the program’s closure is consistent, and secondly arranging for elaboration to happen (these jobs are needed for any Ada build system, but they may be implemented differently).
When using gnatmake, the first of these jobs is usually superfluous, because gnatmake has already organised all the necessary compilations. It is possible to get this wrong (by, for example, moving a unit to a different library and not deleting its compilation products from the original place) but quite hard!
Elaboration is a feature of Ada that isn’t present in many other languages. There’s explanation at gcc.gnu.org and other places, but for a simple example,
with Foo;
package Bar is
Int : Integer := Foo.Value;
[...]
end Bar;
package Foo is
function Value return Integer;
[...]
end Foo;
we don’t know what Foo.Value is going to return at compile time, and we may not know until run time (what if it reads a value from the command line?), so Foo.Value must be in a fit state to be called before Bar’s initialisation happens.
Bar’s initialisation happens when Bar is elaborated, and likewise for Foo, so it’s gnatbind’s job to recognise this and arrange that Foo is elaborated before Bar.
It does this by emitting calls to packages’ elaboration code in a function (usually called adanit), and a main(), which is to be called by the operating system and calls adainit and then the Ada main program, say program.adb.
gnatmake then calls gnatlink, which takes the gnatbind-generated code, in Ada in files called b-program.ad[sb] or b__program.ad[sb] or b~program.ad[sb] depending on the vintage of the compiler, compiles it, and links it with the program’s closure to produce the final executable.
See the four points listed here: https://docs.adacore.com/gnat_ugn-docs/html/gnat_ugn/gnat_ugn/building_executable_programs_with_gnat.html#binding-with-gnatbind
You could think of it as a built-in make but without the recompilation: it ensures objects are consistent, generates a correct initialization order, compiles it, and passes everything to the linker.
As pointed out, in Ada the program entry point is not your main procedure, but one that performs a safe initialization and then calls your main procedure.

Procedure Recursion and Stacking

To model the run-time semantics of procedures, it is known that a stack is generally needed.
1. If the language does not allow procedure recursion, do we have to have stacks?
2. If the language does allow procedure recursion, but a recursive call can only happen at the end of a procedure, do we have to have stacks?
No. However if the language is turing complete you can simulate one if you want. Eg. Brainf**k doesn't have a stack and I have made a LISP1 interpreter with it that supports procedures and macros.
It depends. Traditionally you need a way to have multiple nested arguments and that is a stack by definition. If your procedures have no arguments and only can call something else in the end of it's code It's almost as if procedures give less value than a goto would. However if you mean tail instead of end then it would be just as good as a goto without arguments.

When to use Pragma Pure/Preelaborate

Is there a set of general rules/guidelines that can help to understand when to prefer pragma Pure, pragma Preelaborate, or something else entirely? The rules and definitions presented in the standard (Ada 2012), are a little heavy-going and I'd be grateful to read something that's a little more clear and geared towards the average case.
If I wanted to be thorough without fully understanding the "why" of it, can I simply try:
Mark the package spec with pragma Pure;
If it doesn't compile, try pragma Preelaborate;
If that fails, then I've done something tricky and either need to pragma Elaborate units on a with-by-with basis, or rethink the package layout.
While this might work (does it?), because it's recommended to mark a package as Pure whenever possible (likewise with Preelaborate), however it seems a bit brain damaged and I'd prefer to understand the process a bit better.
pragma Pure
You should use this on any package which does not have an internal state. It tells the user of the package that calls to any subprograms cannot have side effects, because there is no internal state they could change. So a function declared at library level inside a pure package will always return the same result when called with the same parameters.
The Ada implementation is allowed to cache return values of functions of a pure package, and to omit calls to subroutines if their return values won't be used because of these requirements. However, you can violate the constraints by calling imported subroutines (e.g. from a C library) inside your pure package (these may change some internal state which the Ada compiler doesn't know of). If you're evil, you can even import Ada subroutines from other parts of the software with pragma Import to bypass the requirements of pragma Pure. Needless to say: If you're doing anything like this, don't use pragma Pure.
Edit: To clarify the circumstances when calls may be omitted, let me quote the ARM:
If a library unit is declared pure, then the implementation is permitted to omit a call on a library-level subprogram of the library unit if the results are not needed after the call. Similarly, it may omit such a call and simply reuse the results produced by an earlier call on the same subprogram, provided that none of the parameters are of a limited type, and the addresses and values of all by-reference actual parameters, and the values of all by-copy-in actual parameters, are the same as they were at the earlier call. This permission applies even if the subprogram produces other side effects when called.
GNAT, for example, additionally defines that any subroutines that take a parameter of type System.Address or a type derived from it are not considered pure even if they are defined in a pure package, because the location the address points to may be altered, but GNAT does not know what kind of structure the address points to and therefore cannot run any checks about whether the referenced value of the parameter has been changed.
pragma Preelaborate
This tells the compiler that the package won't execute any code at elaboration time (i.e. before the main procedure starts executing). At elaboration time, the following constructs will execute:
Initialization of library-level variables (this can be a function call)
Initialization of tasks declared at library level (they may start executing before the main procedure does)
Statements in a begin ... end block at library level
You generally should avoid these things if you don't need them. Use pragma Preelaborate wherever possible, it tells the caller that he can safely use the package without executing anything at elaboration time.
If something doesn't compile with one of these pragmas when you think it should, look into why it doesn't compile. It may help you discover problems with your package implementation or structure. Don't just drop the pragma when it doesn't compile. As the constraint affects possible constraints on any packages that depend on yours, you should always choose the strictest applicable pragma.
Elaboration Order Handling in GNAT is a helpful guide. Ideally, the standard rules will suffice for most programs. The pragmas tell the compiler to substitute your elaboration order. They should be applied to solve specific problems, rather than used empirically.
Addendum: #ajb underscores an important distinction among the pragmas. The article cited agrees with the approach outlined in the question (bullets one and two): "Consequently a good rule is to mark units as Pure or Preelaborate if possible, and if this is not possible, mark them as Elaborate_Body if possible." It goes on to discuss situations (bullet three) "where neither of these three pragmas can be used."

How does functional programming avoid state when it seems unavoidable?

Let's say we define a function c sum(a, b), functional programming -style, that returns the sum of its arguments. So far so good; all the nice things of FP without any problems.
Now let's say we run this in an environment with dynamic typing and a singleton, stateful error stream. Then let's say we pass a value of a and/or b that sum isn't designed to handle (i.e. not numbers), and it needs to indicate an error somehow.
But how? This function is supposed to be pure and side-effect-less. How does it insert an error into the global error stream without violating that?
No programming language that I know of has anything like a "singleton stateful error stream" built in, so you'd have to make one. And you simply wouldn't make such a thing if you were trying to write your program in a pure functional style.
You could, however, have a sum function that returns either the sum or an indication of an error. The type used to do this is in fact often known by the name Either. Then you could easily make a function that invokes a whole bunch of computations that could possibly return an error, and returns a list of all the errors that were encountered in the other computations. That's pretty close to what you were talking about; it's just explicitly returned rather than being global.
Remember, the question when you're writing a functional program is "how do I make a program that has the behavior I want?" not, "how would I duplicate one particular approach taken in another programming style?". A "global stateful error stream" is a means not an end. You can't have a global stateful error stream in pure function style, no. But ask yourself what you're using the global stateful error stream to achieve; whatever it is, you can achieve that in functional programming, just not with the same mechanism.
Asking whether pure functional programming can implement a particular technique that depends on side effects is like asking how you use techniques from assembly in object-oriented programming. OO provides different tools for you to use to solve problems; limiting yourself to using those tools to emulate a different toolset is not going to be an effective way to work with them.
In response to comments: If what you want to achieve with your error stream is logging error messages to a terminal, then yes, at some level the code is going to have to do IO to do that.1
Printing to terminal is just like any other IO, there's nothing particularly special about it that makes it worthy of singling out as a case where state seems especially unavoidable. So if this turns your question into "How do pure functional programs handle IO?", then there are no doubt many duplicate questions on SO, not to mention many many blog posts and tutorials speaking precisely to that issue. It's not like it's a sudden surprise to implementors and users of pure programming languages, the question has been around for decades, and there have been some quite sophisticated thought put into the answers.
There are different approaches taken in different languages (IO monad in Haskell, unique modes in Mercury, lazy streams of requests and responses in historical versions of Haskell, and more). The basic idea is to come up with a model which can be manipulated by pure code, and hook up manipulations of the model to actual impure operations within the language implementation. This allows you to keep the benefits of purity (the proofs that apply to pure code but not to general impure code will still apply to code using the pure IO model).
The pure model has to be carefully designed so that you can't actually do anything with it that doesn't make sense in terms of actual IO. For example, Mercury does IO by having you write programs as if you're passing around the current state of the universe as an extra parameter. This pure model accurately represents the behaviour of operations that depend on and affect the universe outside the program, but only when there is exactly one state of the universe in the system at any one time, which is threaded through the entire program from start to finish. So some restrictions are put in
The type io is made abstract so that there's no way to construct a value of that type; the only way you can get one is to be passed one from your caller. An io value is passed into the main predicate by the language implementation to kick the whole thing off.
The mode of the io value passed in to main is declared such that it is unique. This means you can't do things that might cause it to be duplicated, such as putting it in a container or passing the same io value to multiple different invocations. The unique mode ensures that you can only ass the io value to a predicate that also uses the unique mode, and as soon as you pass it once the value is "dead" and can't be passed anywhere else.
1 Note that even in imperative programs, you gain a lot of flexibility if you have your error logging system return a stream of error messages and then only actually make the decision to print them close to the outermost layer of the program. If your log calls are directly writing the output immediately, here's just a few things I can think of off the top of my head that become much harder to do with such a system:
Speculatively execute a computation and see whether it failed by checking whether it emitted any errors
Combine multiple high level systems into a single system, adding tags to the logs to distinguish each system
Emit debug and info log messages only if there is also an error message (so the output is clean when there are no errors to debug, and rich in detail when there are)

Resources