Hello, declarative world
codon.com
codon.com
What you're saying amounts to basically an instruction not a relation or declaration in the programming sense of the word.
<div visible="after:5pm">
hello world
</div>Of course, that lets you specify the execution time, but unless you have a system consuming the format that is aware of the additional semantics beyond the base HTML semantics, specifying that won't do anything.
You could say main=printf("Hello World"), which declares that your program is equivalent to the action of printing "Hello World".
You could also say that main=sleep(1), which declares that your program is equivent to the program sleep(1).
You can also say that main=sleep(1) >> printf("Hello World"), which says that your program is equivent to the program that sleeps for a second, then prints "Hello World".
You can even create syntactic sugar around the concatenation operator, so it looks like you are programming in an imperative language.
If the concatenation operator happens to be over-loadable, you can do a lot of neat stuff with it. Even if it isn't, you can define common control flow operations (such as for loops) at the language level, which means that you can just as easily define new control flow operations that the language designers didn't think of.
int main() { sleep(1); printf("Hello World"); return 0; }
And then you can say "pure functions can be evaluated in any order". Maybe impure functions can be suffixed by a !. print "Hello World"
with suitable syntax tweaks, is imperative, functional (that code works in Haskell), declarative, and logical (Prolog has a print). Or, more accurately, is simply too small to be usefully discussed in this context. You have to pick something larger to talk about.Most programmers use SQL on a daily basis and we don't hear arguments that it should be procedural to be inline with our other languages.
(Not familiar with databases at all)
As I re-read it, read this article, and pondered some more, I'm revising my view a bit, and beginning to think of "declarative" as something like probability. This is debatable, but I tend to agree with the idea of probability as a measure of knowledge of a given agent, and all probabilities must be understood in the context of some agent for whom those are the correct probabilities, as opposed to being free-standing numbers.
Similarly, declarative can be seen as relative to the programmer. Haskell code that looks like
map (+1) . filter isEven . map (*2)
may be declarative to one programmer while writing it, who doesn't care about the details, yet be essentially imperative to someone who, say, is tasked with optimizing that, and cares about exactly what the compiler does with that code and how. It's not a characteristic of the code, but the programmer's relationship to the code, which may even change over time.From this perspective, most things that proudly wear the label "declarative" can be seen as over-forcing you to be ignorant about their implementation details by making this unnecessarily opaque, for the purpose of wearing that label. Still not a tradeoff I like, vs. the same tech except transparent.
What's nice about this is that it removes the "programmer" from the equation and instead augments language with a bouquet of interpretations. Another Haskell interpretation, e.g., is the static nature of its typing relation.
This is a good way of "understanding monads" as well. They're an algebraic structure which captures just enough to let us talk about a variety of sequential/operational semantic choices for our syntax. At the end of the day, though, they're just data and we can imbue them with all of the semantic notions we enjoy via the interpreter pattern. This is even more emphasized with the "Free Monad" pattern which lets the structure of the monad be very, very close to the syntactic notion.
Implicitly, I'm invoking the idea that a programmer is looking at a particular runtime.
I feel like I have to put that in, or the whole distinction between declarative and imperative completely collapses anyhow. "X.sort()" conceptually simply invokes sorting behavior, possibly with a certain set of guarantees, but in practice, it's very unusual for that to be running so abstractly that the programmer can not penetrate down to relevant runtime details. And when that does happen, generally forces are created that start pushing all the switchable runtimes towards bug-for-bug compatibility, e.g. the web browser's Javascript runtime.
It's not a "bad" thing that it collapses in that case, it just does, that is, consider this the mathematical "collapses" rather than the architectural. You have to have a certain amount of specificity in my opinion or there is no reasonable way to discuss "declarative" vs. "imperative".
A good example of this would be to favor a templating language to render your HTML where possible, rather than building your HTML directly from your code using string concatenation.
Consider a website. An entire website can be modeled as a function that takes headers, URL and request body as parameters and returns headers and body as the response.
You can use the same principle to model almost anything. You take real inputs as inputs and output instructions for imperative side-effect to a minimalist agent of some sort.
I will mention two things that give some clarity about the potential of declarative and intelligent techniques:
1/ Z3[1] (satisfiability modulo theories solver) where you declare what you want and it contains many strategies and previous knowledge to achieve the goals.
2/ Gregory Chaitin[2] saying (can't find the source, probably was a lecture I attended) something like that artificial intelligence is an engineering problem, may be we need a lot of ugly pieces to make it work instead of elegant algorithms.
So I think nobody is working really hard at a higher level to look at the problems we have and find a declarative way to solve them. This solution probably will require a lot of field research and ugly disparate modules.
One of the biggest motivation for them is mathematics. All mathematics is written declaration on paper, and mathematics accomplishes so much, so we should follow that model.
They fail to realize that math on paper is only half the story. The other half is the 'computation' going on in the mind of the mathematician. And even mathematicians haven't reduced it into declaration.
Bottomline: Hybrid approach is superior to 'pure-computational' or 'pure-declarative'.
miniKanren solves a lot of the limitations that systems like Prolog had, at the cost of some performance. The old miniKanren web page explains, "KANREN is a declarative logic programming system with first-class relations, embedded in a pure functional subset of Scheme. The system has a set-theoretical semantics, true unions, fair scheduling, first-class relations, lexically-scoped logical variables, depth-first and iterative deepening strategies. The system achieves high performance and expressivity without cuts."
https://stackoverflow.com/questions/28467011/what-are-the-ma... talks a bit more about the differences.
(Also, the article didn't mention Prolog. What's up with that?)
(I don't have any experience with miniKanren myself.)
> μKanren is part of the miniKanren family of languages. The full miniKanren language has more complex primitives that can be built out of the μKanren ones I’ve shown you.
I appreciate this post, because it works from first principles (close enough) and moves toward more abstract concepts. Good storytelling guide here.
Maybe there is a good reason for this beyond something simple like inertia? On the other hand I sure don't want to be stuck doing backward things—wait a minute—is this more pop culture masquerading as insight?
2.With "declarative", (e.g. SQL) you're preparing input data -NOT describing an algorithm- for a runtime or a compiler or a server, which in turns will run “von Neumann style” algorithms composed from your declarations.
If you need to optimize the last drop of resource usage and speed you use 1).
Besides, there's little debate to be had on what's the best model: all of these are built on hardware that's interacting functional programs (combinatorial logic) and finite state machines (sequential logic). How they're combined varies on a chip by chip basis. Underneath, though, everything boils down to a careful combination of functional and semi-functional programming implemented in silicon. Except analog which is purely functional. ;)
Note: This brings out another angle whereby we use tens of thousands to billions of circuits running concurrently in a functional way to simulate a sequential, imperative machine that does way less in a clock cycle. This is why porting the algorithms onto FPGA's almost always has significant speed-up unless the imperative machine is insanely optimized (i.e. Intel/AMD/IBM). It's also why even prototypes like SHard produce efficient, better-than-generic hardware from functional language specifications of algorithms. Maybe we should just go functional all the way and invest money into CPU's which do that with utmost efficiency, eh?
Are you saying there is no debate that functional is the best model because that is what is going on in the hardware?
I haven't heard that point of view before.
Does Knuth's use of MIX in the AOCP mean he doesn't share that view or does it mean something else?
LISP is not declarative, I think.
I recall doing automatic programming research long ago. We got to the 4GL's that automated much of what you want. There were even programs that took requirements put into CASE tools and synthesized simple code to meet them. Eventually we found that we'd have to precisely specify behavior, requirements, and constraints w/ heuristics-driven tools synthesizing the system. Probably led to concept for 5GL's being constraint-programming.
In any case, if you look at all of that, functional programming or 4GL combined with a formal specification language + good QA tools are MUCH EASIER. One person told me you'd have to think like a lawyer to do English precise enough declare an app into existence. While there's even non-technical people cranking out working, readable code in Python and Ocaml. Cleanroom's restrictive method got similar quality out of languages not known for it. I think combining approaches like that is better than declarative approach for the general case w/ DSL's (in LISP etc) being used for declarative programming of specific components (GUI's, database integration).
The constraints and heuristics mean you're always doing more work than mere declarative programming outside simple stuff like SQL.
It was only by butting my head against mathematics again and again, and by easing my way into functional programming via Javascript that I began to understand declarative thought, and what it meant to perceive logic in a static picture rather than as the expression of some concrete action. I still don't get it it's foreign to my way of thinking, but I know what it looks like now.
I enjoyed this article a great deal, but my hackles did rise somewhat at the author's assumption that declarativism is somehow closer to human thinking than imperativism. It is more abstract, yes, and maybe more familiar to a natural mathematician, but I do not believe declarativism has a special status with regard to human thought.
I don't think it would be a poor hypothesis to suggest that many people reason using a much softer, more "narrativ-istic" kind of thinking than is represented by mathematics. This kind of thinking is closer to imperative programming which often seems like you are telling a story about how to solve a problem - the details of the computer's memory and CPU being the indispensable forest and woodcutter's axe of the tale.
I think that a lot of the proponents of declarative programming (and functional programming for that matter) are mathematically talented people who are cognitively inclined to understand things in that way - just as the positive features of imperative (and object oriented) languages are similarly attractive and seemingly fundamental, to other kinds of thinkers.
Take for example the proof there are infinitely many primes. "Assume there are only finitely many primes (assume: imperative). Let them be called p_1,...,p_n (let: imperative). Let q=(p_1...p_n)+1 (let: imperative). Then q is not divisible by any of p_1,...,p_n, so q is prime (this is the only non-imperative line in the proof). Contradiction, discard the assumption and conclude there are infinitely many primes (imperative)."
The biggest "non-imperative" feature of mathematics is the way functions are first-class objects. But that's only considered to be "non-imperative" because of a historical fluke, there's no intrinsic reason why "first class functions" = "non-imperative", it's just that the early languages happened to be that way.
Here's some other examples from those pages:
https://en.wikipedia.org/wiki/Declarative_programming
https://en.wikipedia.org/wiki/Database_query_language
https://en.wikipedia.org/wiki/Logic_programming
https://en.wikipedia.org/wiki/Inductive_programming
You'll probably have the most fun with those. Might take you months to dig through all of that if you're following up on the various types with further research. You'll also be one of the only ones that know what the hell you're talking about when topic comes up next in form of "what can or can't it do?" ;)
Written by a functional programming fellow, Robert Harper, this post is not a rant around the claim we can't escape the procedural effectful world. It's rather a call for the awareness that the "how" is a fundamental issue in programming.
I haven't written a Prolog program of any complexity myself, but it seems a common complaint is that the programs can be hard to reason about and debug. Your solutions are always running on top of a unification algorithm.
Further, the fact that prolog-like languages (be kanren-like languages part of those or not) have issues should not be a basis for discarding all of them. Useful niches can be found, and their problems might be solved.
More in general, the idea that I can take a function, and provide an argument less, and a result, and the function is inverted (in a mathematical sense) for free, seems very, very powerful. The main alternative is to have two functions, one the inverse of another, that use the same building blocks for reuse, but that puts a cognitive overhead to the program, whereas a person understands most inversions quite easily.
That said, most work that's successful avoids first-order logic in favor of tools such as Isabelle/HOL or Coq. They also support extraction to Ocaml, etc w/ certified compilers in works for them. Maybe better to use those for logic programming. I'm not a domain expert in that field so can't be sure.
For people doing first-order, though, the Mercury language and Visual Prolog app are best I know of with plenty examples.
Multi-paradigs languages, if done right, combine the best of two imperative & declarative worlds beside they're very friendly to beginners and they don't have this intimidating quality that other "pure" languages like Haskell have.
Also, procedural coding is easy to do and carries low mental overhead in writing and not every programming problem has be modeled in functional terms or worldview to be solved. Sometimes, it's economical and efficient just to get the work done in two lines of code without going through all those functional hoops just to capture a field value in some form.
Javascript supports functional style programming, but it is still in the context of an imperitave language. In the same way, Haskell supports imperitive programming, but it has to be done within the context of a functional language.
Also, in my opinion, the intimidating part of Haskell is not its purity, but rather its type system.
Btw, a universal machine for all paradigms was already done with RISE CPU's tagged/typed operations + Ten15 VM. I'd like to see an optimized version of Ten15 get takeup. Javascript, on other hand, is garbage that's a LCD at best. Even Oberon in the browser (Juice project) outdid it in about every attribute.