Compare that the "functional programming" which in comparison has a glas clear definition, which makes it much easier to decide if some program is "pure functional" or not, avoiding prolonged and unproductive discussions.
Compare that the "functional programming" which in comparison has a glas clear definition, which makes it much easier to decide if some program is "pure functional" or not, avoiding prolonged and unproductive discussions.
Does it?
Human language is unsuitable for "glass clear", unambiguous definitions, just because of its inherent fuzziness (which is nota bene an essential property for efficient communication).
Not really. FP has an obvious connection to lambda calculus. Many OOP languages don't even have a straightforward notion of "function", which is what lambda calculus is all about.
> There is a well-established theory of functions, the λ-calculus, ... However, our theory of objects is self-contained; it is the first that does not require explicit reference to functions or procedures.
So that answers your question in the negative and proves my point: no, not every language is based on lambda calculus.
See e.g. Landin, P. J. (1965): A Correspondence between ALGOL 60 and Church's Lambda-notation.
No.
Prolog et. al. is based on Horn Clause representation and evaluated using something called SLD resolution.
"Concatinative" languages (like Joy) are based on function composition (not application.)
(FWIW, the Turing machine is not based on Lambda calculus. Not that TMs are a programming language.)
I sometimes wonder about the possible form of the "Platonic Ideal" is that each of these systems represent. George Spencer-Brown's Laws of Form seems to me to be the ultimate concrete example, but that's not a universally held opinion. :)
It's like Roman numerals vs. base-10 numerals, eh? "XXIII" and "23" both denote the same number (which may or may not exist depending on your metaphysical outlook) but neither notation can be said to be "the" notation.
let x = e
y = e
in ...
is not equivalent to let x = e
y = x
in ...
if your semantics can distinguish expressions by the time they take to evaluate.Still (to address your original question) people do consider lambda calculi with effects, so if the expression-based fragment of an imperative language supports lambdas with lexical scoping then sure you could get away with saying that "their expression are based on lambda calculus".
So taking eg. Template Haskell, you actually loose referential transparency since then even the syntax itself matters and you can’t just replace it to an equivalent expression. I have no experience with Template Haskell itself so bare with me, but eg. having a macro m that converts the constant 2 to the string “two” while others to empty string will fail to be referentially transparent since `m 2` will not be the same as `let a = 2 in m a`.
let a = 2 in $(m a)
and since `a` is not in scope at the time the TH splice runs compilation will fail. Secondly, even if you could write it, I don't really think something that contains Template Haskell should be called a "Haskell expression".With regard to pron's comment, he has a long history of being technically correct with regard to Haskell. The operative point is
> What they mean is that the language is referentially transparent (like most language) and a term's reference (denotation) is an object value in the language
i.e. Haskell is referentially transparent with respect to a particularly coarse semantics (value semantics).
Anyway, your greater point that referential transparency doesn't imply immutability is completely correct.
It's a programming paradigm based on simple, well-understood mathematical framework. It's also the starting point for most research in programming language theory.
“A closure can capture constants and variables from the surrounding context in which it’s defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.”
https://docs.swift.org/swift-book/LanguageGuide/Closures.htm...
What makes you think Swift only supports functional programming?
> I would have used Haskell doc reference
And if you would have, you would have disproved your own point. In Haskell, closures cannot mutate state, because mutable state is not part of the language semantics. It's modeled explicitly via monads.
I'll deliberately avoid terms so we don't get to argue about words, but rather substance.
1. Objects have identity that's independent of their state (two objects with same state are still distinct).
2. Objects contain implementation which is hidden, including optionally mutable state.
3. Communication occurs through handlers/methods/calls, which provide only INDIRECT access to the object's internal state.
4. Objects can stand for one another, if their handlers react in a compatible way with the object they're replacing.
I tried to keep it as basic as possible, while avoiding topics about inheritance and what not.
Now the question is "do you need an OOP language to do OOP", and no, you don't. There's overlap. For example if you take the above, you'll notice that closures in FP match all of those except one thing: mutable state. So if your FP language allows mutable state, then you can do OOP in it with closures. And that's not a proof that OOP is not a thing, rather it's a proof that what some small minds keep putting in opposition, things like FP vs OOP vs DOP are just pieces of a larger puzzle that forms a single cohesive picture.
It may seem like a weak criteria if you're using to OOP/FP languages that provide typesafe, efficient polymorphism out of the box. But the reason we have Obj C and C++ is that polymorphism (while also maintaining encapsulation etc.) is a living nightmare in a language like C.
Immutability, first class functions, laziness are all such tools. And which tools are used can vary, just like the tools that help "doing OO" can vary.
But I think that FP does have that core which has a glass clear definition. I'm not sure which is the core of OO. Maybe it's the idea of "encapsulating" mutation (basically inverting the core of FP). Maybe it's the principle of virtual methods / messaging. It's not so clear I think.
> Some FP is lazy, some isn't.
The definition of FP implies that laziness is irrelevant. A language can enforce a pure functional style without being lazy or not.
> Some is immutable, some isn't
That's wrong. And it is a good example of why FP is much clearer defined. Referential transparency implies immutability. So there is no pure FP code that uses mutability.
> Some claim it's a style in any language, others that it's a type of language.
It's neither. It is a property of a program. Some languages enforce this property, others make it impossible to write a full program in this style. But that's independent of the definition itself.
I hope that explanation sheds a bit of light into it.
Most real-world programming requires handling of side effects somewhere, so such programs have parts which are not referentially transparent.
It's why I distinguished between "core" and "meat" in my other response. Functional programming languages offer features that help making a larger part of the program referentially transparent (and, optionally, to ensure that property in the types).
> Nowadays the word "functional" is used different from the original meaning (which some now call "pure functional")
I don't think the meaning changed, just the tools offered and the scope of the support they offer to achieve referential transparency (pure functions). Early Lisp offered expression oriented programming which was a step towards it, but used dynamic scoping which isn't really helping. Later on both Lisps and ML offered lexical scoping. Then the purely functional trend came up via Miranda and (Clean and) Haskell, the latter encoding purity in the type system. In the last decade mainstream programming languages added functional programming features but are falling short in encoding purity in the types (just as Lisp never did), which means that it's (easily) possible for the abstractions offered (both functions but also libraries built on iterators) to not be referentially transparent, manual care (and/or culture) is required instead. The tooling doesn't help enough to guarantee referential transparency. But the core idea, the target of the efforts, is the same.
> Referential transparency implies immutability.
I didn't go into immutability in my other response because you can use mutation inside pure functions, as long as the effect doesn't leave the function. Building referentially transparent functions does not imply using immutable data structures or variable bindings inside.
That's not correct though. Look at Haskell (or even stricter Idris). This is real world application code that can do whatever Java, C++, ... can do, but it is pure functional and hence doesn't contain any non referentially transparent parts. Just because an application is written in a pure functional style does not mean that it cannot e.g. write into a database when it is executed.
Of course many languages don't even allow to use a fully pure functional style. In that case, a more or less big functional "core" is the closest one can come to a pure FP style.
> I didn't go into immutability in my other response because you can use mutation inside pure functions, as long as the effect doesn't leave the function.
Sure you can do that and sometimes it is even the best practical solution. And the function might even be referential transparent - but your whole application is now not pure functional anymore, because a part of it is not referential transparent anymore.
Hence: if an application has to satisfy the criteria of being "pure functional" then every expression must be referential transparent and hence no mutation can be used.
If you use the state monad, you encode local side effects. Then you use runState in a function that has a non-monadic type--a pure function.
Feel free to argue that the state monad is only having pure expressions because it can be represented using only pure functions and partial evaluation. For all practical purposes it is used to write code that exerts (local) side effects into the state maintained by the state monad.
You can a function in say, OCaml, that fills an array via mutation using only function arguments to deduct the contents, and then returns that array. That function is pure (it exerts no side effects outside and is not exposed to side effects from the outside, its result value only depends on the function argument values). Yet how is that not using mutation, non-referentially transparent parts inside?
You can write the same function with a mutable vector in Haskell (Data.Vector.Mutable, using the ST monad). How is it not exactly the same code as the one in OCaml except for the types that guarantee the purity? Yes, it may be using pure expressions underneath to guide the type system to enable the proof of purity. But does that change what I said? Both to the programmer and (after optimization) to the machine the code is using mutation. You can model mutation using only pure functions. That may be useful in some ways, but you can also take a C program, represent every memory address as an entry in a tree of immutable nodes, the outside world as an abstract entity with versions, and recreate it with only pure expressions. That may be what is useful in some contexts for developing proofs or to implement type systems, but is this relevant for a programmer interested in writing more functional programs? Why not just say that using ST is using local mutation (that the type system can guarantee will not leak outside) and be done with it?
x = expression
y = expression
...
is the same as x = expression
y = x
...
and that, secondly x = expression
...
is the same as ...
when x does not appear in ... . Those are the important properties of Haskell (with regard to this discussion). Whether this advances the discussion is another matter ...I would say pure FP languages such as Haskell are actually the best for such a case. Because in these languages the order/timing of lines of code are decoupled from the execution order/timing.
... That's good in your opinion?
But when the percentage of concurrent code grows bigger, then it is better - because you lose the LoC<->execution relation anyways.
You can for example see that in Python. Python makes it really hard to break out of that model and have a line of code running while another one is running. That's why paralle and async/concurrent programming is such a PITA in python.
So, in my opinion for some problems the FP style is much better, because instead of "working your way around", you embrace the fact that LoC and execution are inherently decoupled.
It's what the current discussion is about, since I explained why I explicitly didn't make a statement about immutability being required for FP.
> The operative point is that, in Haskell, firstly
> x = expression
> y = expression
> ...
> is the same as
> x = expression
> y = x
> ...
Sure but that's the pure part of a program.
x <- expression
y <- expression
is not generally the same as x <- expression
let y = x in ...
and I pointed out that you can have such a program (i.e. with mutability) inside a function that is pure to the outside.The interesting feature of Haskell / Monads is that you can have mutability be both inside and around pure expressions (impure code making use of pure code and then impure code again inside the implementation of pure code). Not everything in a Haskell program needs to be pure. Of course, pure expressions are good since they are easier to reason about. Also, they are the core idea of FP as I mentioned earlier. But, I want to avoid people taking away the impression that you then never can use mutable data structures, and that's why in my original comment I didn't make a statement about immutability being required for functional programming.
Functional programming has a core idea and that is pure expressions (referential transparency), and then features to help increase the area of programs which are pure expressions and increase the confidence / safety of them being so. With regards to that confidence, Haskell goes far (there's still unsafePerformIO (and non-total functions) so it's not completely perfect). And I'm not disputing that when I say that it still is and always was called functional programming when there were no language-provided features to give that confidence, or only "half-assed" ones if one wants to call them that. Or only culture and convention. My point in the whole discussion is to explain why functional programming doesn't mean the same thing for everyone, and how it can both be that @slver says "there's no clear definition of FP" and that there is at the same time. There is a clear definition for referential transparency, which is the core idea of FP. But the term FP also encompasses various approaches and tools with varying qualities to enable that core idea, and it's here where there's no clear definition.
Yes, but as long as this is done with only pure expressions, then the whole program is pure functional - that is what the definition is about.
> You can a function in say, OCaml, that fills an array via mutation using only function arguments to deduct the contents, and then returns that array. That function is pure (it exerts no side effects outside and is not exposed to side effects from the outside, its result value only depends on the function argument values). Yet how is that not using mutation, non-referentially transparent parts inside?
The guarantee or at least convention of referential transparency for every expression has benefits. It means I as a developer can make certain assumptions about code that I could not make otherwise. I can do certain refactorings and changes while being sure that it will not change the program semantics.
As soon as parts of the program is not pure anymore, I know have to always re-assure myself that I'm currently working with the pure part of the code. Same for any tooling that I build.
As I said, you can make that choice and sometimes it is the best thing to do - but you cannot call your application "pure functional" anymore when you do.
> That's wrong. And it is a good example of why FP is much clearer defined.
OCaml and F# are two examples of functional languages with freely supported mutable data types. So maybe what I said is "wrong" about your personal idea of what FP is.
But it's clearly not "wrong" about the fact some people see immutability as optional for FP, which is the very point I'm making.
Both OCaml and F# are multi-paradigm languages that have FP features, but are not restricted to FP. The support for mutability comes from their imperative/OOP ancestors, not from FP.
See the top answer here: https://stackoverflow.com/questions/210835/what-is-referenti...
The problem is even called out in the answer itself:
> For example, our example "Edinburgh has been the capital of Scotland since 1999" signifies the fact that "capital of Scotland" depends on the time at which it is being considered. Such context-dependence is a reality, both in natural languages and programming languages.
"Therein lies the rub." Such context-dependence doesn't have to be a reality in programming languages. "2 + 2 = 4" is an eternal verity, not subject to context-dependence. The whole point of FP (going back to Backus' Turing Award paper where he introduces and defines "FP") is to operate in the pure realm of [binary Boolean] logic.
The latter is (quoting pron’s older comment: https://elarib.com/item?id=22141647 ): “an expression e is referentially transparent if any sub-term in it can be replaced with any other having the same reference (aka denotation) without changing e's reference”. Perhaps I should have linked this comment over the stackoverflow answer, as it also mentions how eg. template haskell or LISPs are actually NOT necessarily referentially transparent due to macros. So while in Java replacing the constant 2 with a variable having value 2 is always equivalent (in reference, not necessarily effect!), the same may not be true with a given macro applied.
Could you point out a specific point in my answer that is not compatible / or contradcits with the common definition of referential transparency?
> what you mean is simply side-effect freeness
This really makes it sound like an assumption. And:
> So just exchanging ref. transparency with “not having side effects” in your otherwise perfectly fine and interesting reply will make it correct
You implying my answer is incorrect here but you are not giving an example of what is wrong even though I asked for it.
That being said, I believe that the definition of referential transparency (and therefore FP) is not useful at all without making "semantics" a part of it. And this is the point where discussion can happen. For example, is the performance characteristics of a function part of the program semantics? I would say no. Even though it matters in practice. Same goes for logging: one can argue that logging (that can not blow up due to e.g. lack of disk space) can be considered irrelevant for program semantics and hence a function that logs something with a "print" can still be pure.
If you remove semantics from the definition then the definition becomes rather useless imho. In that case I would rather change its meaning or switch to a different definition alltogether.
And I agree, we do have to specify what is the denotation of an expression that shall remain constant after replacing a sub-expression with that part's denotation -- for example choosing value of expression after evaluation is not too interesting, as it is shared by the majority of languages, and can only be "violated" by macros for example. Choosing the denotation as "value and side-effects" doesn't add much over saying simply that the language is pure and as you also note, purity has different levels.
> Choosing the denotation as "value and side-effects" doesn't add much over saying simply that the language is pure
You are aware that, regarding the discussion between us two, it was you and not me who even used the term "side-effect" first, right?
https://news.ycombinator.com/item?id=27414992
> FP means that every expression (= part that can be evaluated) is referential transparent
> Referential transparency implies immutability
Both of these statements are false. As I said, Java is referentially transparent.
addOne(2)’s value is the same as the value of the last line in `var a = 2; addOne(a)`. And Java is most definitely not a pure FP language, and its referential transparency doesn’t imply immutability. You can argue that inserting `int two() { sideEffect(); return 2; }` ‘s invocation in place of 2 will change the semantics of the program but that is precisely the meaning of a side-effecting function — so using referential transparency is redundant in that meaning.
I disagree with you - I think you are using a different definition and there seems indeed to be discussion about what the definition really is. Wikipedia for examples uses the definition that I use, but in the Talk-section someone made a similar complaint to yours (or was that you :)). [https://en.wikipedia.org/wiki/Referential_transparency]
I stick to my definition though, because... I find it more useful and I think it is the more common one.