Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

"Declarative programming" at its core seems to be about separating what the code says from what it does, and trying to put more "magic" in between the saying and the doing. But that's not a binary distinction for all kinds of reasons. What a code "says" is already a bit subjective to start with, but then we get into things like, is this "declarative"?

    def SayHello():
        print("Hello!")

    SayHello()
After all, when I type "SayHello()", I'm not specifying how I want Hello to be Said, just that I want it said. Yet, of course, if this is called "declarative" than the word is stripped of all meaning, and we do clearly want it to mean something.

Personally I'd set the line as being somewhere where by definition of the system, there is a barrier set up and you are no longer allowed to "see into" the implementation because it's going to do things so crazy to your declaration that allowing you to see into it would ruin it. e.g. in SQL when you send in your query the query engine is reserving the right to rewrite it in absolutely crazy ways, and in principle, you have no right to ask about what it actually did as long as you get the "right answer". This is because I then like to segue into how that barrier is somewhat illusory because it's always going to take some concrete actions and you may have need to see into those actions. But that isn't the only place to draw the line, it's just where I find it convenient to draw the line so I can ride a pet hobbyhorse. There isn't really a bright line, even though there is clearly some real difference we are trying to talk about.



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]:

  void concatenate(struct node *a,struct node *b)
  {
      if (a->next == NULL)
          a->next = b;
      else
          concatenate(a->next,b);
  }
And here's the same thing in Prolog:

  append([],Ys,Ys).
  append([H|T],Ys,[H|Zs]):-  
    append(T,Ys,Zs).
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].

________________

[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).




Consider applying for YC's Winter 2027 batch! Applications are open till November 2.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: