He talks about how lisps are bootstrapped on a small core, and that is nice. Reminded me that this is the approach that Perl 6 is trying to take (NQP is the core) although on a larger and more complicated language
You would only want to execute an expression and throw away its value for its side effects.
Useful for IO as there's normally nothing I want to pass the value of a println expression to, evaluating already prints it to stdout. Also useful for mutation, but doing this in Clojure feels wrong since the immutable data structures are among the language's greatest strengths.
It honestly hadn't occurred to me that printing to stdout could fail, or that I'd need to check its return value to ensure it was successful. Can you think of an situation where that might happen? And if so, why doesn't println throw an exception?
On the other hand, a side effect like writing to a file or inserting to a database can easily fail, but those of course throw exceptions.
Output redirected to a file and file being already closed, for one. Or anyway, stdout being closed midway due to many other possible reasons: this is (probably) why printf returns a number of bytes written or -1, for example: http://www.cplusplus.com/reference/cstdio/printf/
The question is whether there's anything that can be done about it. Much of the time, there is! And people overlook it more often than they should. But much of the time there isn't, and in that case it might as well be ignored.
I just started learning Clojure, so I don't really understand this article very well. Is Clojure significantly different from other LISPs (e.g. Racket)?
Actually - I didn't read the article very carefully, so I may be completely wrong here - it looks like the OP uses a bit incorrect term. To me it looks like the word he wants is "applicative", and the opposition to it would be "concatenative" (see: http://evincarofautumn.blogspot.com/2012/02/why-concatenativ...).
"Imperative" is usually contrasted with "declarative" (and not with "functional", which TFA seems to suggest) and in practice most languages are imperative to some degree, even the ones which are said to be "declarative" are most often not purely so (but see Datalog).
The author of this article is straining logic to argue that Clojure is not a functional language. It's true that Clojure is not a purely functional language, but most people should already know that. Unlike Haskell, Clojure allows side affects in code. It exposes the underlying Java platform 'transparently' which is definitely imperative. In that respect it's not too much different from other Lisps which allow side affects and generally have an FFI of some sort for working with imperative code written in other languages.
Unlike other Lisps, though, Clojure's core semantics are very strongly functional. All of Clojure's core data structures and types are immutable and to have mutable state you need to resort to reference types with special semantics to have mutable state. It would be uncomfortable to write Clojure code in a primarily imperative fashion. Common Lisp by contrast has no problem with imperative code or mutable state.
Ah, thanks. It does look pretty functional from my limited experience so far, plus I think these debates are very academic. You can write purely functional Python if you want, it's just going to be neither easy nor practical.
Which language is not imperative then? The only program writtten in non-imperative language I can think of would return random values (of random type, also not known to mankind, including total destruction and creation of life) and crashed avery second function call.
Prolog functions are a little more advanced 'if' statements. Flow of the execution is always the same. Data may be processed in a different order, but the rest is purely imperative. Logic never changes.
You're implying that the opposite of "imperative" is "nondeterministic." That's incorrect.
Prolog is declarative as opposed to imperative. User source code declares rules/constraints, and the Prolog compiler performs a search algorithm to arrive at a solution that satisfies the constraints.
Given the same input Prolog program and imperative (Pascal?) one will give the same results. Because Prolog is imperative.
Non-imparative languages give estimated results (actually I don't know any of these). Why? Because for example data is too large to do simple things like counting all elements. Thats non-imperative. Result is no longer based on what is in the data space, but based on its characteristics. For example, you may calculate a number of helium atoms in a cubic inch, but you will never do such thing like counting them one by one (imperative way).
aminit I am not sure if you know what imperative means.
Imperative programs have a state or virtual machine that is manipulated by the program. All programs, at heart, are imperative, but higher level constructs allow for procedural or declarative programming which abstracts away the lower-level imperative bits.
// Imperative: (essence of it)
x = 0;
x = x + 1;
return x;
// Procedural
x = 0;
inc(x);
return x;
// Functional
return inc(x);
// declarative
select x + 1;
---
My argument is that all of these above are imperative. Down below sits imperative CPU which will give exactly the same result. Every time.
Wiki definition of imperative programming is wrong: "Imperative programming is focused on describing how a program operates"
This applies to every program in every language. Because you as a programmer are always focused on describing how program operates. Prolog is only another higher abstraction layer.
// Imperative: (essence of it)
x = 0;
x = x + 1;
return x;
// Procedural
x = 0;
inc(x);
return x;
// Functional
return inc(x);
// declarative
select x + 1;
---
My argument is that all of these above are imperative. Down below sits imperative CPU which will give exactly the same result. Every time.
Wiki definition of imperative programming is wrong: "Imperative programming is focused on describing how a program operates"
This applies to every program in every language. Because you as a programmer are always focused on describing how program operates. Prolog is only another higher abstraction layer.
// Imperative: (essence of it)
x = 0;
x = x + 1;
y = x;
// Procedural:
x = 0;
inc(x);
return x;
// Functional:
return inc(x);
// Declarative:
select x + 1;
---
My argument is that all of these above are imperative. Down below sits imperative CPU which will give exactly the same result. Every time.
Wiki definition of imperative programming in my opinion is too broad: "Imperative programming is focused on describing how a program operates"
This applies to every program in every language. Because you as a programmer are always focused on describing how program operates. Prolog is only another higher abstraction layer.
Wiki:
"... imperative programming is a programming paradigm that describes computation in terms of statements that change a program state."
Lets reverse the logic here. Non-imperative programming will not describe computation in terms of statements that change program state. So, non-imperative program will not use statements.
Down below sits imperative CPU which will give exactly the same result. Every time.
If you're authoring programs for an abstract virtual machine in a declarative and/or functional style, it doesn't necessarily matter to you that that the underlying CPU architecture is imperative in nature. That's the point of an abstraction layer.
When we say a language is declarative vs. imperative, we're talking about the language level itself, not the full stack all the way down to the metal. A programming language and an implementation are different things.
Yes, but only because the underlying machine architecture is imperative, right? If you had a chip that could evaluate pure functions with no mutable state (like a Lisp Machine that used a purely functional dialect of Lisp) then you could have programs with no underlying imperativeness.
But in a perhaps more relevant sense, in languages like Haskell or Prolog it is possible to write a program with no imperative state manipulation happening at the programmer's working level of abstraction.
Still, this really has nothing to do with determinism.
Also, I like SQL as an example of declarative programming.
SELECT * FROM USERS
WHERE JOIN_DATE = "20150106"
That is a declarative program; the SQL engine it's run on decides the underlying imperative implementation of the solution at runtime. It is also deterministic in that when it's run on the same database containing the same data multiple times it will return the same result.
From what I know about Prolog, I understand it's similar in this regard.
Yes, good point, but this was not meant to be an example of declarative paradigm, just functional paradigm. It's SML, which is not a particularly declarative language. Haskell is a little bit more declarative but AFAIK its pattern matching works the same way as SML.
There's a case to be made that Haskell (or Idris) is not imperative, but 'imperative', at it's core, describes an interface, and Haskell supports that interface, basically (and a limited version of the associated semantics -- all good interfaces reduce their detail and surface area, though, no good having an arbitrarily precise interface).
Haskell is not imperative, though it allows you to express imperative programs within it via Monads. One way to think of Haskell is as a purely functional, lazy analogue to the C Preprocessor. Your program is then merely a function whose value is a set of instructions for performing the effects you desire on some particular machine. The cool thing about being lazy in this context is that preprocessing time (evaluation of the function) and runtime are interleaved rather than separate (as in the case of C).
"imperative" does not seem like a good/valuable characterization. Yes, the "do" form is imperative. That's why you're not supposed to use it much, perhaps only for the top-level structure of your program, the part that acts as an interpreter to connect the pure program to the outside world. When you express business logic in a language like clojure, that expression of the business logic is not usually "a sequence of commands executed in order". It usually looks more like "an expression tree and rules for how nodes are evaluated".
The downside of a simple, transparent language like lisp is that it exposes you to the dangerous parts of the underlying. An opaque abstraction (e.g. expressing database operations as a tree of operations some of which define transaction boundaries, rather than as a series of SQL commands some of which open or close transactions) is vital for transforming dangerous things into safe things. Ultimately a program looks like an expression of business-level logic and then an opaque layer that pretends the computer is actually a machine that understands your business. Lisp forces you to provide more of that opacity yourself, whereas a language that already has a vocabulary for drawing distinctions between different kinds of thing provides you some of the scaffolding you need (the tradeoff being of course that it will be less specialized to your particular circumstances).
Just because Clojure has the DO form (or that Common Lisp has the PROG form) doesn't mean that the language is imperative. It means that it allows imperative programming.
This is like saying that a language with casts doesn't have a type system. No, it has a type system, it just also has an escape hatch that lets you break out from the type system when you need to. Lisps' functional-ness is generally the same sort of thing: They are designed primarily around function-oriented programming, but provide straightforward escape hatches into imperative programming. Common Lisp tends more towards being multi-paradigm than Clojure (i.e. provides more imperative accommodations), but both have strict evaluation, which the author seems to equate with imperative-ness.
The author spends more time talking about a small core and non-leaky abstractions than about imperative-ness. On these points, I totally agree and think it's a well-written article. I don't exactly see the connection with being imperative, and I think it's a shame that the title will draw attention away from those points. The thing that I really like about working in Clojure is how the abstractions are built with care: I feel like whenever I just use the base building blocks, they fit together seamlessly and edge cases usually don't come up.
I do think that it's meaningful to describe a program as being imperative or declarative or object-oriented or functional or strongly-typed or whatever, and that determination can be separate from the language that the program was written in (up to a point). See also, "writing COBOL in any language."
But just because those descriptions apply to programs, that doesn't mean that they are inapplicable to languages. At the least, those languages can be described in terms what types of programs they allow and what types they encourage, and how strongly. What programs they allow is a question mostly of features, but what they encourage is more complicated question that often involves the culture and ecosystem as much as the syntax or standard library.
E.g., Haskell encourages functional, strongly-typed programs. It's possible to write an imperative, weakly-typed program by using a lot of monads and passing data between modules as serialized strings. But that program is massively cutting against the grain, so to speak, so it's still meaningful to describe Haskell as functional and strongly-typed. Common Lisp functions naturally fit together into a functional program, but it's nowhere near as difficult or counter-intuitive to write an imperative program, so Common Lisp can lay better claim to being multi-paradigm.
I do think that programs are the primary things that are described as imperative or whatever. Languages are better-described by the programs they encourage rather than the features they have. For example, JavaScript and Ruby have all the features common to (dynamically-typed) functional languages (closures, lambdas, functions-as-values, etc), but that's not the type of program that is easiest to write in either language. That's why most people don't describe JavaScript as a functional language, even though there are certainly functional programs written in it.
From what I've read, the exact definitions of imperative and declarative vary a lot. The most useful rule of thumb I've come across is to consider imperative to be concerned with HOW something is to be done, while declarative concerns primarily WHAT is supposed to be done.
SQL is usually classified as declarative in that you're not telling the database how to retrieve the top ten entries from a table - you're merely informing it that you are interested in those entries.
It struck me at some point that this definition must be relative to the level of abstraction you're operating at. For example, when using (go …) blocks, I'm declaring that I want the code contained within the block to be processed asynchronously, but I'm not describing how that is to be done. Perhaps a callback might be described as being "more imperative" since it contains more implementation details?
So, am I correct in thinking that this discussion would hinge on what you consider to be "concrete" and not? That something executes in sequence is hardly enough to decide whether it is to be considered declarative or imperative in nature.
Declarative is what we'd like programming to be in the future. At the highest level we could say "Computer, make me a first person shooter set in WW2" and receive a ready to play game from that.
"Imperative" isn't really a binary feature of programming languages, the imperative-declarative axes is more a continuous axes of variation describing code (which can be applied to languages, as well, as a general characterization of code written in the language.)
And, while its certainly possible to write very imperative code in clojure (or even Haskell!), idiomatic clojure is more declarative and less imperative than most popular languages, so, I think, on balance, "is imperative" is not a useful description.
That being said, in most "declarative" languages, there still remains the risk of being bitten by issues related to order of operations and structure of the code that aren't apparent if you view the code as purely declarative statements of intent rather than as to some degree imperative instructions. And if you call anything that presents that issue as "imperative", virtually all programming languages are "imperative".
'That being said, in most "declarative" languages, there still remains the risk of being bitten by issues related to order of operations and structure of the code that aren't apparent if you view the code as purely declarative statements of intent rather than as to some degree imperative instructions. And if you call anything that presents that issue as "imperative", virtually all programming languages are "imperative".'
Yeah, particularly painful is massaging something purportedly "declarative" in order to get particular operational semantics out of it.
I sometimes dream of a future language that will have a denotational layer separated from an operational one, with each playing off the other. In the limit, you can implement things entirely in the denotational and the compiler will fill in the operational details - but when that breaks down you have the option to specify how it works in a way that's built for specifying how it works (and, ideally, then checked against your declarative specification). Ideally here, you can express arbitrarily strong or weak constraints upon both and the compiler will say "done", or "I don't see how", or "I can prove that's impossible."
38 comments
[ 3.0 ms ] story [ 90.2 ms ] threadUseful for IO as there's normally nothing I want to pass the value of a println expression to, evaluating already prints it to stdout. Also useful for mutation, but doing this in Clojure feels wrong since the immutable data structures are among the language's greatest strengths.
On the other hand, a side effect like writing to a file or inserting to a database can easily fail, but those of course throw exceptions.
Actually - I didn't read the article very carefully, so I may be completely wrong here - it looks like the OP uses a bit incorrect term. To me it looks like the word he wants is "applicative", and the opposition to it would be "concatenative" (see: http://evincarofautumn.blogspot.com/2012/02/why-concatenativ...).
"Imperative" is usually contrasted with "declarative" (and not with "functional", which TFA seems to suggest) and in practice most languages are imperative to some degree, even the ones which are said to be "declarative" are most often not purely so (but see Datalog).
Unlike other Lisps, though, Clojure's core semantics are very strongly functional. All of Clojure's core data structures and types are immutable and to have mutable state you need to resort to reference types with special semantics to have mutable state. It would be uncomfortable to write Clojure code in a primarily imperative fashion. Common Lisp by contrast has no problem with imperative code or mutable state.
Prolog is declarative as opposed to imperative. User source code declares rules/constraints, and the Prolog compiler performs a search algorithm to arrive at a solution that satisfies the constraints.
Let me explain:
Given the same input Prolog program and imperative (Pascal?) one will give the same results. Because Prolog is imperative.
Non-imparative languages give estimated results (actually I don't know any of these). Why? Because for example data is too large to do simple things like counting all elements. Thats non-imperative. Result is no longer based on what is in the data space, but based on its characteristics. For example, you may calculate a number of helium atoms in a cubic inch, but you will never do such thing like counting them one by one (imperative way).
Imperative programs have a state or virtual machine that is manipulated by the program. All programs, at heart, are imperative, but higher level constructs allow for procedural or declarative programming which abstracts away the lower-level imperative bits.
No I have not had lunch yet.// Imperative: (essence of it) x = 0; x = x + 1; return x;
// Procedural x = 0; inc(x); return x;
// Functional return inc(x);
// declarative select x + 1;
--- My argument is that all of these above are imperative. Down below sits imperative CPU which will give exactly the same result. Every time.
Wiki definition of imperative programming is wrong: "Imperative programming is focused on describing how a program operates"
This applies to every program in every language. Because you as a programmer are always focused on describing how program operates. Prolog is only another higher abstraction layer.
// Imperative: (essence of it) x = 0; x = x + 1; return x;
// Procedural x = 0; inc(x); return x;
// Functional return inc(x);
// declarative select x + 1;
--- My argument is that all of these above are imperative. Down below sits imperative CPU which will give exactly the same result. Every time.
Wiki definition of imperative programming is wrong: "Imperative programming is focused on describing how a program operates"
This applies to every program in every language. Because you as a programmer are always focused on describing how program operates. Prolog is only another higher abstraction layer.
// Imperative: (essence of it) x = 0; x = x + 1; y = x;
// Procedural: x = 0; inc(x); return x;
// Functional: return inc(x);
// Declarative: select x + 1;
--- My argument is that all of these above are imperative. Down below sits imperative CPU which will give exactly the same result. Every time.
Wiki definition of imperative programming in my opinion is too broad: "Imperative programming is focused on describing how a program operates"
This applies to every program in every language. Because you as a programmer are always focused on describing how program operates. Prolog is only another higher abstraction layer.
Wiki: "... imperative programming is a programming paradigm that describes computation in terms of statements that change a program state."
Lets reverse the logic here. Non-imperative programming will not describe computation in terms of statements that change program state. So, non-imperative program will not use statements.
If you're authoring programs for an abstract virtual machine in a declarative and/or functional style, it doesn't necessarily matter to you that that the underlying CPU architecture is imperative in nature. That's the point of an abstraction layer.
When we say a language is declarative vs. imperative, we're talking about the language level itself, not the full stack all the way down to the metal. A programming language and an implementation are different things.
I like this more for a functional idiom:
Or this: All programs, at heart, are imperativeYes, but only because the underlying machine architecture is imperative, right? If you had a chip that could evaluate pure functions with no mutable state (like a Lisp Machine that used a purely functional dialect of Lisp) then you could have programs with no underlying imperativeness.
But in a perhaps more relevant sense, in languages like Haskell or Prolog it is possible to write a program with no imperative state manipulation happening at the programmer's working level of abstraction.
Still, this really has nothing to do with determinism.
Also, I like SQL as an example of declarative programming.
That is a declarative program; the SQL engine it's run on decides the underlying imperative implementation of the solution at runtime. It is also deterministic in that when it's run on the same database containing the same data multiple times it will return the same result.From what I know about Prolog, I understand it's similar in this regard.
An actual declarative language would choose TUESDAY over _ because it's the most specific match, and not because it matched it first.
There are probably more, I just don't know about them.
The downside of a simple, transparent language like lisp is that it exposes you to the dangerous parts of the underlying. An opaque abstraction (e.g. expressing database operations as a tree of operations some of which define transaction boundaries, rather than as a series of SQL commands some of which open or close transactions) is vital for transforming dangerous things into safe things. Ultimately a program looks like an expression of business-level logic and then an opaque layer that pretends the computer is actually a machine that understands your business. Lisp forces you to provide more of that opacity yourself, whereas a language that already has a vocabulary for drawing distinctions between different kinds of thing provides you some of the scaffolding you need (the tradeoff being of course that it will be less specialized to your particular circumstances).
This is like saying that a language with casts doesn't have a type system. No, it has a type system, it just also has an escape hatch that lets you break out from the type system when you need to. Lisps' functional-ness is generally the same sort of thing: They are designed primarily around function-oriented programming, but provide straightforward escape hatches into imperative programming. Common Lisp tends more towards being multi-paradigm than Clojure (i.e. provides more imperative accommodations), but both have strict evaluation, which the author seems to equate with imperative-ness.
The author spends more time talking about a small core and non-leaky abstractions than about imperative-ness. On these points, I totally agree and think it's a well-written article. I don't exactly see the connection with being imperative, and I think it's a shame that the title will draw attention away from those points. The thing that I really like about working in Clojure is how the abstractions are built with care: I feel like whenever I just use the base building blocks, they fit together seamlessly and edge cases usually don't come up.
I do think that it's meaningful to describe a program as being imperative or declarative or object-oriented or functional or strongly-typed or whatever, and that determination can be separate from the language that the program was written in (up to a point). See also, "writing COBOL in any language."
But just because those descriptions apply to programs, that doesn't mean that they are inapplicable to languages. At the least, those languages can be described in terms what types of programs they allow and what types they encourage, and how strongly. What programs they allow is a question mostly of features, but what they encourage is more complicated question that often involves the culture and ecosystem as much as the syntax or standard library.
E.g., Haskell encourages functional, strongly-typed programs. It's possible to write an imperative, weakly-typed program by using a lot of monads and passing data between modules as serialized strings. But that program is massively cutting against the grain, so to speak, so it's still meaningful to describe Haskell as functional and strongly-typed. Common Lisp functions naturally fit together into a functional program, but it's nowhere near as difficult or counter-intuitive to write an imperative program, so Common Lisp can lay better claim to being multi-paradigm.
I do think that programs are the primary things that are described as imperative or whatever. Languages are better-described by the programs they encourage rather than the features they have. For example, JavaScript and Ruby have all the features common to (dynamically-typed) functional languages (closures, lambdas, functions-as-values, etc), but that's not the type of program that is easiest to write in either language. That's why most people don't describe JavaScript as a functional language, even though there are certainly functional programs written in it.
http://www.lispcast.com/two-kinds-of-bootstrapping
SQL is usually classified as declarative in that you're not telling the database how to retrieve the top ten entries from a table - you're merely informing it that you are interested in those entries.
It struck me at some point that this definition must be relative to the level of abstraction you're operating at. For example, when using (go …) blocks, I'm declaring that I want the code contained within the block to be processed asynchronously, but I'm not describing how that is to be done. Perhaps a callback might be described as being "more imperative" since it contains more implementation details?
So, am I correct in thinking that this discussion would hinge on what you consider to be "concrete" and not? That something executes in sequence is hardly enough to decide whether it is to be considered declarative or imperative in nature.
And, while its certainly possible to write very imperative code in clojure (or even Haskell!), idiomatic clojure is more declarative and less imperative than most popular languages, so, I think, on balance, "is imperative" is not a useful description.
That being said, in most "declarative" languages, there still remains the risk of being bitten by issues related to order of operations and structure of the code that aren't apparent if you view the code as purely declarative statements of intent rather than as to some degree imperative instructions. And if you call anything that presents that issue as "imperative", virtually all programming languages are "imperative".
Yeah, particularly painful is massaging something purportedly "declarative" in order to get particular operational semantics out of it.
I sometimes dream of a future language that will have a denotational layer separated from an operational one, with each playing off the other. In the limit, you can implement things entirely in the denotational and the compiler will fill in the operational details - but when that breaks down you have the option to specify how it works in a way that's built for specifying how it works (and, ideally, then checked against your declarative specification). Ideally here, you can express arbitrarily strong or weak constraints upon both and the compiler will say "done", or "I don't see how", or "I can prove that's impossible."