I must admit that I haven't explored LISP dialects yet, but I was under the impression many of them have side-effects, which would keep them from being considered functional.
Untyped lambda calculus has also been shown to be unsound. Admittedly, so has recursive typed lambda calculus without proof of termination.
All things considered I'd say that type systems can have a smooth learning curve but can provide really useful properties later on (which we have yet to fully explore).
This
Most people trying to learn type theory, category theory and FP at the same time find it too difficult and just abandon.
I will fully agree with. E.g. you can use functors and monads perfectly fine in practice without having a clue about CT.
I was under the impression many of them have side-effects, which would keep them from being considered functional
This is one of the confusion I feel Haskell brings about. Lisp invented functional programming.
The first functional programming language and the second oldest programming language still in use (after FORTRAN), LISP began life in 1958 as a project led by John McCarthy at MIT.
Purity is an orthogonal concept to FP. Haskell brings about a lot of additional orthogonal concepts which people then confuse and mix in their head.
FP is a paradigm. Fundamentally, it requires first class functions. Any language with first class functions can be used to program functionally.
Now side-effects break the purity of the lambda calculus. But FP is not the lambda calculus. The latter is a computation model, the former a paradigm of programming. And so FP requires design patterns to manage side effects. In turn, in a programming paradigm side effect is unavoidable, because computations are not the only thing you want to program the computer to do. Haskell chooses to use monads for that. And it chooses to add additional compiler constraints that enforce their use for side effect. This is not the only pattern one can use to manage side effects in FP. In most Lisps, no single pattern is enforced on you, you can freely explore different ways to deal with it, and choose which one you prefer.
Untyped lambda calculus has also been shown to be unsound. Admittedly, so has recursive typed lambda calculus without proof of termination.
This also seem to stem from a confusion brought about by Haskell. The untyped lambda calculus has no type system. Soundness is irrelevant to it. Soundness is a quality of type systems.
So, I'm not dismissing the potential utility of monads for managing side effects, or compiler awareness of effects, or of type systems, or the applications of category theory within programming, etc. But all these things are better learned (in my opinion), one at a time, or you risk conflating them and lose the ability to distinguish one from the other.
So if you want to learn functional programming. I still think a Lisp is best. As it is as close as you get to the first incarnations of the lambda calculus and of functional programming.
Plus, Lisp can later allow you to add one by one each additional concepts. You can play with monads and see how they can help you manage side effects. Then you can explore the full range of categorical abstractions and see how they can help you structure your code. With Clojure and Racket, you can also slowly introduce a type system on top, and play with its own set of benefits and trade offs. Finally, you can even attempt to build a type system of your own.
I must admit that I haven't explored LISP dialects yet, but I was under the impression many of them have side-effects, which would keep them from being considered functional.
<Clutching My Pearls>OH NO SIDE EFFECTS! HOW DARE YOU THREATEN MY FUNCTIONAL PURITY!</Clutching>
If you don't have side effects, all you've got is a gently warming black box. ;)
Seriously - I'd take a look at Clojure - or at least some of Hickey's videos where he talks about the philosophy behind it. And possibly the ones where he trashes type systems and reclaims FP for dynamic languages. You may not agree with him but it's riveting.
FP is a paradigm. Fundamentally, it requires first class functions. Any language with first class functions can be used to program functionally.
Wikipedia, which is a decent proxy for popular opinion and therefore linguistic semantics, would call that functional style, and places functional programming under the mantle of declarative programming, excluding thereby the existance of side-effects in FP. This is my reasoning for saying impure languages are not functional.
But let's avoid further confusion and call them pure and impure for now.
Impure functional programming is not backed by many of the mathematical properties that make pure functional programming easy to reason about.
When I post about FP and what you can do with it, it is referrring exclusively to pure FP.
I would therefore not recommend my readers to learn languages based around impure FP, as that paradigm can be achieved with more popular languages that one might already know, like JS.
I feel that when you refer to 'pure' FP you are referring to languages with a static type system which is checked on compilation. Probably languages with higher-kinded types. This is useful for writing programs in a functional style as it places hard restrictions on what you can write.
A programmer in a dynamic language can write the same program, in the same style, with the same (mathematical) properties - only they will not be checked by the type system. You will have to limit yourself to using the parts of the language which don't mutate - you will need a bit of discipline. This is easier in a language like Scheme which makes it very clear when you're performing a function which mutates a variable.
It's harder these days to find a language that won't support a functional style of programming - even Java now has first class functions - which is why I prefer to refer to it as a paradigm and not a language feature.
Please, let's put aside the denotational debate, because I'm frankly not interested in it, and it is pointless.
That may be, but you have convinced me! I should be stating pure FP specifically, to avoid confusion :-)
When you said "Haskell is bad for FP", what are the concepts you were referring too? And in what way was Haskell bad for them?
I was referring specifically to (primarily statically typed and compiled) purely functional programming. I don't think Haskell has too much influence over impure languages.
If I had to summarize my issues, I would say that Haskell, for a flagship language, strays too far from the core concepts of purely functional programming / typed lambda calculus. It adds complexity (String/Text, typeclasses, lazyness...), reduces certain arguably desirable properties (errors break programs-as-proofs) and it is inflexible in how it represents the imperative world in a functional context, pushing IMO too much coding in IO monads, which is essentially imperative programming.
Because Haskell is, again, the flagship of pure fp, it really shapes the notion of what pure (typed) fp is. I think this may be bad for adoption and innovation.
At this point, though, I should probably write a new post about what I think purely functional programming could / should be.
Haha, ya, I think if you added pure in front of FP for your post title, all confusion would have been avoided, at least for me.
Especially because I think purity is a hurdle for newcomers, and a hard stepping stone. So I find if people start with impure, and gradually of their own learn the benefits of purity and the mechanism to write more and more pure code, it can be a better stepping stone for them moving to a purely FP language like Haskell. It's a more gradual learning curve, I feel.
So I actually thought that this was what you meant also.
I'd actually be really interested in that follow up post you mention. Because my only experience with purely functional languages is Haskell. And I was under the impression that most of the concepts you say bring too much complexity were actually necessary to upheld purity.
For example, without typeclasses, I thought the type system would be a lot weaker, and you'd lose quit a bit of its expressiveness. That without laziness and non-strict evaluation, you were not able to isolate side effects and extract them out of your functions so they evaluate purely. I also thought you couldn't match the performance of impure algorithms without it. And similarly, without Monads and its syntax sugar over them, IO effects couldn't be tracked and controlled by the type system.
Basically, I've always been under the impression that all of Haskell's complexity was due to it trying to maintain purity at all cost.
So I'm intrigued to learn of alternative ways, that could be arguably simpler, yet would still allow for practical, performant and strictly pure code.
Purity is tied to compilation and static typing, almost by necessity. While I have no idea how a pure language that is dynamically typed would look like, it would still have e.g. explicit dependencies to make refactoring easy, whereas a language with side-effects does not.
Scheme which makes it very clear when you're performing a function which mutates a variable
Depending on how this is implemented, you might call it a linear type or monad in disguise. If side effects are explicit they're not really side effects.
You might be able to make a pure functions, if you don't consider type errors to be side effects (which I do if they don't require explicit catching, which is unlikely in a dynamic language), but that doesn't make the entirety of the language pure.
How are you going to represent effects? As you pointed out you will need at some point. Untyped monads? A framework like TEA but error resistant?
So, you might be able to do it, but it's going to be a bit awkward at least, which is why I said "almost by necessity".
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
I cannot fully agree with what you are saying.
I must admit that I haven't explored LISP dialects yet, but I was under the impression many of them have side-effects, which would keep them from being considered functional.
Untyped lambda calculus has also been shown to be unsound. Admittedly, so has recursive typed lambda calculus without proof of termination.
All things considered I'd say that type systems can have a smooth learning curve but can provide really useful properties later on (which we have yet to fully explore).
This
I will fully agree with. E.g. you can use functors and monads perfectly fine in practice without having a clue about CT.
This is one of the confusion I feel Haskell brings about. Lisp invented functional programming.
From google.com/url?sa=t&source=web&rct...
Purity is an orthogonal concept to FP. Haskell brings about a lot of additional orthogonal concepts which people then confuse and mix in their head.
FP is a paradigm. Fundamentally, it requires first class functions. Any language with first class functions can be used to program functionally.
Now side-effects break the purity of the lambda calculus. But FP is not the lambda calculus. The latter is a computation model, the former a paradigm of programming. And so FP requires design patterns to manage side effects. In turn, in a programming paradigm side effect is unavoidable, because computations are not the only thing you want to program the computer to do. Haskell chooses to use monads for that. And it chooses to add additional compiler constraints that enforce their use for side effect. This is not the only pattern one can use to manage side effects in FP. In most Lisps, no single pattern is enforced on you, you can freely explore different ways to deal with it, and choose which one you prefer.
This also seem to stem from a confusion brought about by Haskell. The untyped lambda calculus has no type system. Soundness is irrelevant to it. Soundness is a quality of type systems.
So, I'm not dismissing the potential utility of monads for managing side effects, or compiler awareness of effects, or of type systems, or the applications of category theory within programming, etc. But all these things are better learned (in my opinion), one at a time, or you risk conflating them and lose the ability to distinguish one from the other.
So if you want to learn functional programming. I still think a Lisp is best. As it is as close as you get to the first incarnations of the lambda calculus and of functional programming.
Plus, Lisp can later allow you to add one by one each additional concepts. You can play with monads and see how they can help you manage side effects. Then you can explore the full range of categorical abstractions and see how they can help you structure your code. With Clojure and Racket, you can also slowly introduce a type system on top, and play with its own set of benefits and trade offs. Finally, you can even attempt to build a type system of your own.
<Clutching My Pearls>
OH NO SIDE EFFECTS! HOW DARE YOU THREATEN MY FUNCTIONAL PURITY!</Clutching>
If you don't have side effects, all you've got is a gently warming black box. ;)
Seriously - I'd take a look at Clojure - or at least some of Hickey's videos where he talks about the philosophy behind it. And possibly the ones where he trashes type systems and reclaims FP for dynamic languages. You may not agree with him but it's riveting.
Wikipedia, which is a decent proxy for popular opinion and therefore linguistic semantics, would call that functional style, and places functional programming under the mantle of declarative programming, excluding thereby the existance of side-effects in FP. This is my reasoning for saying impure languages are not functional.
But let's avoid further confusion and call them pure and impure for now.
Impure functional programming is not backed by many of the mathematical properties that make pure functional programming easy to reason about.
When I post about FP and what you can do with it, it is referrring exclusively to pure FP.
I would therefore not recommend my readers to learn languages based around impure FP, as that paradigm can be achieved with more popular languages that one might already know, like JS.
I feel that when you refer to 'pure' FP you are referring to languages with a static type system which is checked on compilation. Probably languages with higher-kinded types. This is useful for writing programs in a functional style as it places hard restrictions on what you can write.
A programmer in a dynamic language can write the same program, in the same style, with the same (mathematical) properties - only they will not be checked by the type system. You will have to limit yourself to using the parts of the language which don't mutate - you will need a bit of discipline. This is easier in a language like Scheme which makes it very clear when you're performing a function which mutates a variable.
It's harder these days to find a language that won't support a functional style of programming - even Java now has first class functions - which is why I prefer to refer to it as a paradigm and not a language feature.
Please, let's put aside the denotational debate, because I'm frankly not interested in it, and it is pointless.
Let's discuss the concepts and intended meanings instead. And so let me ask you a question:
When you said "Haskell is bad for FP", what are the concepts you were referring too? And in what way was Haskell bad for them?
That may be, but you have convinced me! I should be stating pure FP specifically, to avoid confusion :-)
I was referring specifically to (primarily statically typed and compiled) purely functional programming. I don't think Haskell has too much influence over impure languages.
If I had to summarize my issues, I would say that Haskell, for a flagship language, strays too far from the core concepts of purely functional programming / typed lambda calculus. It adds complexity (
String
/Text
, typeclasses, lazyness...), reduces certain arguably desirable properties (errors break programs-as-proofs) and it is inflexible in how it represents the imperative world in a functional context, pushing IMO too much coding in IO monads, which is essentially imperative programming.Because Haskell is, again, the flagship of pure fp, it really shapes the notion of what pure (typed) fp is. I think this may be bad for adoption and innovation.
At this point, though, I should probably write a new post about what I think purely functional programming could / should be.
Haha, ya, I think if you added pure in front of FP for your post title, all confusion would have been avoided, at least for me.
Especially because I think purity is a hurdle for newcomers, and a hard stepping stone. So I find if people start with impure, and gradually of their own learn the benefits of purity and the mechanism to write more and more pure code, it can be a better stepping stone for them moving to a purely FP language like Haskell. It's a more gradual learning curve, I feel.
So I actually thought that this was what you meant also.
I'd actually be really interested in that follow up post you mention. Because my only experience with purely functional languages is Haskell. And I was under the impression that most of the concepts you say bring too much complexity were actually necessary to upheld purity.
For example, without typeclasses, I thought the type system would be a lot weaker, and you'd lose quit a bit of its expressiveness. That without laziness and non-strict evaluation, you were not able to isolate side effects and extract them out of your functions so they evaluate purely. I also thought you couldn't match the performance of impure algorithms without it. And similarly, without Monads and its syntax sugar over them, IO effects couldn't be tracked and controlled by the type system.
Basically, I've always been under the impression that all of Haskell's complexity was due to it trying to maintain purity at all cost.
So I'm intrigued to learn of alternative ways, that could be arguably simpler, yet would still allow for practical, performant and strictly pure code.
Regards!
Haha, I guess that is a point in favor of the idea that Haskell is too dominant. :-)
Purity is tied to compilation and static typing, almost by necessity. While I have no idea how a pure language that is dynamically typed would look like, it would still have e.g. explicit dependencies to make refactoring easy, whereas a language with side-effects does not.
Depending on how this is implemented, you might call it a linear type or monad in disguise. If side effects are explicit they're not really side effects.
How is this impure?
You might be able to make a pure functions, if you don't consider type errors to be side effects (which I do if they don't require explicit catching, which is unlikely in a dynamic language), but that doesn't make the entirety of the language pure.
How are you going to represent effects? As you pointed out you will need at some point. Untyped monads? A framework like TEA but error resistant?
So, you might be able to do it, but it's going to be a bit awkward at least, which is why I said "almost by necessity".