My opinion is of course coloured by my experience with Prolog but the way I
understand "declarative programming" describes a style of coding rather than
any particular programming language features. Hiding the details of execution
is not necessary and the compilers and interpreters of most languages do that
anyway.
To give an example of what I mean, here's a typical C function that
concatenates two linked lists [1]:
append/3 is a Prolog predicate that can be used to concatenate two lists. It's
a little difficult to get your head around it the first time you see it but
basically one interpretation of it is that concatenating a list Ys and the
empty list yields the list Ys; and concatenating the non-empty list [H|T] (for
"Head" and "Tail") with a list Ys yields the concatenation of the tail, T, of
[H|T], with Ys.
append/3 and concatenate() share the same procedural interpretation: "to
concatenate two lists walk through the first list until you find the last
element in its tail ("[]" in Prolog, NULL in C) and make that element be the
first element of the second list [2]".
The difference is that Prolog's declarative interpretation describes not only
concatenation of two lists, but also their difference. Indeed, append/3 can be
called in different instantiation modes to perform both tasks:
% Zs is the concatenation of _Xs and _Ys
?- _Xs = [a,b,c], _Ys = [d,e,f], append(_Xs,_Ys,Zs).
Zs = [a, b, c, d, e, f].
% Zs \ Xs = Ys
?- _Xs = [a,b,c], _Zs = [a,b,c,d,e,f], append(_Xs,Ys,_Zs).
Ys = [d, e, f].
% Zs \ Ys = Xs
?- _Ys = [d,e,f], _Zs = [a,b,c,d,e,f], append(Xs,_Ys,_Zs).
Xs = [a, b, c] .
% Starting a variable with an underscore, like in _Xs, _Ys, _Zs, stops the
% Swi-Prolog runtime from printing the bindings of the variable and
% cluttering the screen with values you already know ;)
In short, the definition of append/3 doesn't (just) tell the computer to
concatenate two lists to yield a third list, like the C program does. Instead,
it describes a relation between three lists that is true under certain
conditions, specified by the programmer who wrote append/3. It's this property
of describing the shape of data, rather than splitting your code into data and
operations on data, that mark code as declarative rather than procedural [3].
[2] "Behind the scenes" both Prolog interpreter and C compiler have the same
"view" of computer memory and the pointers from an element in a list to the
next. However, the point here is not that the Prolog interpreter "hides" this
fact. If you squint a bit- it doesn't. [H|T] is the concatenation of H and T
and you can very well read it as a pointer from H to the first element of T.
[3] And note of course there are more purely declarative languages
than Prolog, like Answer Set Programming. But, I know Prolog and You Can't
Teach an Old Dog New Tricks (old dogs know all the tricks :P).
To give an example of what I mean, here's a typical C function that concatenates two linked lists [1]:
And here's the same thing in Prolog: append/3 is a Prolog predicate that can be used to concatenate two lists. It's a little difficult to get your head around it the first time you see it but basically one interpretation of it is that concatenating a list Ys and the empty list yields the list Ys; and concatenating the non-empty list [H|T] (for "Head" and "Tail") with a list Ys yields the concatenation of the tail, T, of [H|T], with Ys.append/3 and concatenate() share the same procedural interpretation: "to concatenate two lists walk through the first list until you find the last element in its tail ("[]" in Prolog, NULL in C) and make that element be the first element of the second list [2]".
The difference is that Prolog's declarative interpretation describes not only concatenation of two lists, but also their difference. Indeed, append/3 can be called in different instantiation modes to perform both tasks:
In short, the definition of append/3 doesn't (just) tell the computer to concatenate two lists to yield a third list, like the C program does. Instead, it describes a relation between three lists that is true under certain conditions, specified by the programmer who wrote append/3. It's this property of describing the shape of data, rather than splitting your code into data and operations on data, that mark code as declarative rather than procedural [3].________________
[1] Copied from: https://www.codesdope.com/blog/article/concatenating-two-lin...
[2] "Behind the scenes" both Prolog interpreter and C compiler have the same "view" of computer memory and the pointers from an element in a list to the next. However, the point here is not that the Prolog interpreter "hides" this fact. If you squint a bit- it doesn't. [H|T] is the concatenation of H and T and you can very well read it as a pointer from H to the first element of T.
[3] And note of course there are more purely declarative languages than Prolog, like Answer Set Programming. But, I know Prolog and You Can't Teach an Old Dog New Tricks (old dogs know all the tricks :P).