
Once you go functional, you can never go back - vsakos
http://zx.rs/5/Once-you-go-functional,-you-can-never-go-back/
======
ccurtsinger
The "six distinct features of a functional language" are
misleading/inaccurate:

1\. Laziness: Not required. See Lisp, Scheme, ML, OCaml, etc.

2\. Functions as first-class citizens: This is probably the only hard and fast
requirement.

3\. No Side Effects: Not required. Again, see Scheme, ML, and Rust.

4\. Static Linking: Certainly not, but the author seems to mean static
_binding_ , which is more important. However, a functional language doesn't
actually need any binding aside from function invocation (see Lambda
Calculus). `Let` bindings are generally available and very useful.

5\. No explicit flow control: Function invocation _is_ flow control. Loops
aren't functional, but some functional languages have them.

6\. No commands/procedures: If the author means "no top-level function
definitions" that is clearly not true. Some functional languages even have
macro languages.

This article gives the (incorrect) impression that functional programming is
about a set of restrictions you must follow 100% of the time. Functional
programming is a style that can be used in any language, as long as you can
pass functions around as values. It wasn't pretty, but Java <=1.7 could still
support a functional programming style by using `Callable` objects.

The `map` and `reduce` operations are certainly possible in imperative
languages. Python has them built-in, they can be written in C++, and so on.

~~~
FullyFunctional
There isn't consensus that Lisp, Scheme, ML, and O'Caml are functional.
Period. There certainly is consensus everywhere that they aren't _purely_
functional. Purely functional doesn't require nor implies lazy, but the two
tends to go together for good reasons.

"Functional programming is a style that can be used in any language"

No, I'm sorry, it's not. [Purely] Functional programming provides guarantees
and properties that are only valid if the necessary discipline is enforced.
Your list of languages are merely procedural languages with functions.

------
sriku
I feel a bit sorry for people getting introduced to "functional programming"
from the imperative world. The plethora of language features available in FLs
distracts them from the main _endeavour_ of FP - which is modeling your
problem domain in terms of function _composition_.

With that perspective, one can do FP in pretty much any language and reap all
of its conceptual and engineering benefits. Without that perspective, one can
end up completely avoiding functional modeling of the problem domain at hand
when using a language tailored to FP and end up faring no better than when
using a language encouraging imperative style.

FWIW ..

> Fibonacci sequence in LISP-oid Scheme

.. has exponential complexity whereas the C version runs in O(n).

    
    
        long fib(long n) {
            return n < 2 ? n : fib(n-1) + fib(n-2);
        }
    

.. is the C equivalent of the same badly performant fib implementation.

~~~
wz1000
Here's a memoized Fibonacci in Haskell

    
    
        fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
    

This is essentially equivalent to

    
    
       long memotable[]; // filled with -1, with first elements 0 and 1
       long fib(int n) {
           if(memotable[n] != -1)
               return memotable[n];
           memotable[n] = fib(n-2) + fib(n-1);
           return memotable[n];
       }
    

Edit: Actually, the Haskell version can be O(1) in memory as the front of the
list can be garbage collected, while the C++ version is O(n) in memory.

~~~
sriku
> Edit: Actually, the Haskell version can be O(1) in memory as the front of
> the list can be garbage collected, while the C++ version is O(n) in memory.

Do you expect the following code to run in constant space?

    
    
        head (drop 1000000000 (let fibs = 0 : 1 : zipWith max fibs (tail fibs) in fibs))
    

Neither ghci nor ghc seems to run it in constant space.

~~~
tome
That's because the entries in fibs are thunks which become very large. This
runs in constant space:

    
    
        zipWith' f (x:xs) (y:ys) = let r = f x y in r `seq` r : zipWith' f xs ys
        head (drop (10 ^ 9) (let fibs = 0 : 1 : zipWith max fibs (tail fibs) in fibs))

~~~
sriku
Nice. .. and you meant to type zipWith' on the second line.

~~~
tome
Yeah I did!

------
RodgerTheGreat
> Since this is impossible in imperative languages, I can't give you a
> comparison.

I'll bite. Here's foldr() in JavaScript, written in an imperative style:

    
    
        function foldr(func, list) {
        	var r = [];
        	for(var index = list.length-1; index >= 0; index--) {
        		r = func(list[index], r);
        	}
        	return r;
        }
    

It isn't defined via pattern matching, but it's absolutely a higher-order
function. Functional programming is a collection of ways of thinking about
problem solving, and it can be applied to any language.

~~~
Kenji
I like what you are doing here. Since I've been introduced to Haskell, I
wondered about functional programming in JavaScript. To my surprise,
JavaScript is versatile enough to support a lot of functional concepts!

~~~
Nemcue
I can recommend flicking through [https://leanpub.com/javascript-
allonge/read](https://leanpub.com/javascript-allonge/read) \-- it's a really
good book on functional programming in JS. Ends up doing Ycombinators,
trampolining etc.

~~~
braythwayt
Actually, the original version of this book is no longer for sale. The link
for the updated free version is here:
[https://leanpub.com/javascriptallongesix/read](https://leanpub.com/javascriptallongesix/read)

Or, if you want to buy it in other formats, here:
[https://leanpub.com/javascriptallongesix](https://leanpub.com/javascriptallongesix)

------
david-given
IIUIC, the example they give of recursively calculating the Fibonacci series
is pretty much _the_ textbook example of how not to use recursion --- it's
O(2^n) and doesn't use tail calls, which means it's slow and will gobble
memory.

Actually calculating the Fibonacci series efficiently in a non-lazy functional
language looks surprisingly non-trivial. In Haskell it's pretty simple:

[http://blog.srinivasan.biz/software/fibonacci-numbers-the-
sl...](http://blog.srinivasan.biz/software/fibonacci-numbers-the-slow-way-or-
the-fast-and-lazy-way)

~~~
cygx
The Haskell code in question:

    
    
        fibs = 1:1:zipWith (+) fibs (tail fibs)
    

It's also a common showcase for Perl6 lazy lists:

    
    
        my @fibs = 1, 1, * + * ... *;

~~~
cygx
Note that Perl6 uses big integers by default, so this will work even beyond
the limit n=92 at which 64-bit integers will overflow.

~~~
dllthomas
Haskell does this, too.

------
Xixi
I love FP, but it always seems a bit disingenuous to claim that it is "pure".
Most programs raison d'être is to do IO (that is to say to have side-effects),
so by construction most programs are impure.

Bjarne Stroustrup has a say about C++: you don't pay for what you don't use. I
like to think about it that way for FP: you don't pay for side effects you
don't use.

~~~
tempodox
FP languages are pure to different degrees: Looking at Haskell, each time you
actually want to do something (change the state of the world), you are
severely beaten with a shower of monads that make side effects hell, even if
the specific side effect you need is as trivial (and innocuous) as they come.
That is, while you might not pay for side effects you don't use, you are
unreasonably overcharged for the side effects you do use, even if they're not
worth the trouble (which is why I don't use Haskell).

OCaml otoh gives you the choice to turn down the purity a bit (allowing side
effects in principle) and even gives you some imperative primitives (viewer
discretion is advised). It gives you the benefits of FP without the
unreasonable costs that Haskell imposes. I assume other FP languages can do
that, too.

[Edit: Clarification.]

~~~
codygman
> each time you actually want to do something (change the state of the world),
> you are severely beaten with a shower of monads that make side effects hell,
> even if the specific side effect you need is as trivial (and innocuous) as
> they come.
    
    
        readFile "/etc/issue" >>= putStrLn
    

Man, I feel so beaten.

~~~
tempodox
Ha, just put this code into any old deeply nested function that's not already
contaminated with the IO monad. Or try to do something that needs two monads
at the same time.

~~~
codygman
> Ha, just put this code into any old deeply nested function that's not
> already contaminated with the IO monad.

Why would I want to do that? I could just pass the filepath to that function
if it needed it, or if I want it to log messages I could use the writer monad.

> Or try to do something that needs two monads at the same time.

Monad transfomers aren't so hard:
[http://www.cs.virginia.edu/~wh5a/personal/Transformers.pdf](http://www.cs.virginia.edu/~wh5a/personal/Transformers.pdf)

Edit: One of my favorite papers, I believe multiple monads are used in it:
[http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/ba...](http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf)

------
tormeh
>As you can see, notation in functional languages is much closer to classic
mathematical notation.

I'm yet to be convinced that this is a good thing.

~~~
xamuel
In the example in question, the author compares apples to oranges: an
imperative iterative Fibonacci print-first-n-fibs vs. a functional recursive
Fibonacci return-nth-fib. An imperative recursive Fibonacci return-nth-fib
would be far closer to classical mathematical notation.

A good way to cut through the ideological BS is to look at how mathematicians
write pseudo-code. It's almost always either pure imperative, or imperative
with a tiny smattering of OOP/FP stuff (no thicker than you'd see in a
sophisticated C project).

~~~
jjaredsimpson

        int fib(int n)
        {
        	return (n <= 1) ? n : fib(n-1) + fib(n-2);
        }

------
Fiahil
As someone who worked with OCaml and C++, and now working with Scala, I must
concur.

First weeks or months are hard, because you have a lot of new mechanism (and
syntax) to learn. Then, when things gets easier, designing reusable components
with function composition become almost systematic. In the end, you'll be able
to write code that feels like poetry (it's very difficult to put words on that
feeling, so I understand if it sounds like a bad piece of propaganda to you).

~~~
emsy
I'm interested if Scala's OOP hybridity leads to better readable code on a
high level of abstraction than for example Haskell? Or do you mainly avoid the
OOP features of the language?

~~~
marcosdumay
You can do OOP-like code on Haskell too... Just don't show your code for
experienced Haskell developers.

~~~
codygman
If it's the right solution then an experienced Haskell developer will
congratulate you. If it's the wrong solution they'll point you towards a
functional solution which likely works better with the language/paradigm.

~~~
marcosdumay
That was intended as a joke, but not completely. Emulating OOP seems to be
deeply frowned upon by Haskell developers.

That is, until you actually do it; I don't think I ever saw people complaining
about any actual instance of OOP-like code, just about the idea.

~~~
codygman
> Emulating OOP seems to be deeply frowned upon by Haskell developers.

Are you sure? I don't think many frown upon the Yi developers for it:

[http://yi-editor.github.io/2014/09/05/oop/](http://yi-
editor.github.io/2014/09/05/oop/)

> That is, until you actually do it; I don't think I ever saw people
> complaining about any actual instance of OOP-like code, just about the idea.

Sometimes there is something to complain about ;)

I remember listening to a Haskell Cast with Michael Snoyman and/or Gabriel
Gonzalez of the Conduit and Pipes fame... whoever it was mentioned that their
early designs were very imperative because they thought it was necessary for
performance.

However over time a functional API/core emerged and was more stable and
performant.

------
kenjackson
This statement seems wrong:

"Functional programming languages fall under the declarative languages
category, meaning algorithms are written in form of relations between steps"

The author also notes that productivity is a pro of FP. While I like much of
the elegance of languages like Haskell and Lisp, its never been clear if it is
actually more productive in writing real code -- and if so is it grossly more
productive, or only by small margins.

~~~
the_af
The only evidence of productivity boost I know of is the following paper by
Paul Hudak and Mark P. Jones:
[http://www.cs.yale.edu/publications/techreports/tr1049.pdf](http://www.cs.yale.edu/publications/techreports/tr1049.pdf),
where the conclusions seem to support the "gross improvement" hypothesis :)

In the paper, the Haskell implementations beat several other languages in
multiple aspects:

\- They are shorter.

\- They are complete (i.e. compile and actually work).

\- They include extra features.

This would seem to show Haskell is actually a pretty great language for rapid
prototyping!

Unfortunately the experiment is marred by several SERIOUS flaws:

\- The requirements were pretty vague, and it was pretty much up to each
implementer how much to implement, and what.

\- The results were self-reported. Here I trust Paul Hudak not to lie about
Haskell, but it's a huge flaw in the study.

\- The review board didn't attempt to execute the code, and in the case of
Functional Programming, they didn't fully understand it and thought some of it
was trickery (they called it "too cute for its own good").

At the end of the paper, the authors propose a new study to remedy these
problems, but unfortunately it never happened as far as I know.

------
ht_th
The difference between his two fibonacci implementations is recursiveness, not
"fp-ness". You could write fib in a imperative style that is close to the
mathematical definition and readable to boot, for example, in ruby:

    
    
        def fib(n) 
            case n
            when 0, 1
                n
            else
                fib(n-1) + fib(n-2)
            end
        end
      
        puts fib(10) # => 55

~~~
codygman
Why do you say this is an imperative style? Nothing is being mutated, just
accumulated.

~~~
ht_th
That's an interesting question, now you've made me think about it. Let me
write the same program in ALGOL 60 first, which would have been considered an
imperative language and was relatively "close to the metal" to our standards:

    
    
        integer procedure Fib (n); integer n;
        begin
            if n = 0 then
                 Fib := 0
            else if n = 1 then
                 Fib := 1
            else Fib := Fib(n-1) + Fib(n-2);
        end Fib
            

Besides the obvious differences in syntax, I'd say that the
procedure/method/function/subroutine is the same. Still, a more fundamental
difference is the "distance" from the machine. In ALGOL 60 we almost see
through it the implementation in machine code / assembler (or whatever mix
between the two were actually used in those days) where a call to a subroutine
meant mutating state: the calls, their arguments, and the results had to kept
track of on the stack. For ALGOL 60 programmers of the time, their operational
understanding of this snippet and ALGOL was imperative.

Compare that to Ruby in its modern context. Implementation details have been
abstracted away in both language and in programmers' operational understanding
of how the example and Ruby works. If I use and understand a machine that
speaks Ruby—a way to look at using a programming language is to imagine a
machine that has that programming language as its machine code—, there's no
mutation, only a variable sequence of calls to fib written succinctly in a
recursive manner. How's that different from functional programming? To be
honest, I don't know. Maybe we should start interpreting "imperative" and
"functional" differently than our predecessors did. Or, maybe these
distinctions are getting less and less meaningful the more abstract a language
or computing environment?

(As an aside, it is also interesting you use "accumulated", as for most ALGOL
60 programmers the accumulator in the arithmetic unit of their massive
computers would take a prominent place.)

------
lispm
Seeing all this incorrect Scheme code hurts...

    
    
        (define (lastHalf L N)
          (if (= N 0) L); Base Case
          (if (or (= N 1) (< N 2))
              (cdr L)
              ;else
              (lastHalf (cdr L) (- N 1)))
              )
    
        Would be:
    
        (define (last-half L N)
          (if (= N 0)
             L
            (if (< N 2)
              (cdr L)
              (last-half (cdr L) (- N 1)))))

------
torus
While Haskell is undoubtedly better suited to writing concise implementations
of sorting algorithms, the C++ versions given could have been written in a
much shorter and clearer fashion.

~~~
vidarh
For C/C++ all that is usually needed is to implement a few helper functions.

E.g. the quicksort example is one of my favourite pet peeves, because the
_reason_ the C version sorts in place is that the archetypical C
implementation after Hoare sorts in place.

Many of the FP versions of quicksorts like this end up requiring a lot of
additional memory. Maybe it is becoming reasonable to assume they will
optimise it into an in-place sort these days, but it didn't use to be.

Meanwhile, with a couple of small, generic helper functions, you can get down
to nearly the same complexity in C/C++. For C++ the only helper you absolutely
need to cut down on the verbosity has been a part of the standard library
since C++98

I wrote a blog post about this back in 2005 [1] about one of the other typical
Haskell quicksort examples, and referenced an article by Alexandrescu that
gave an example implementation of quicksort in C++ with the (generic)
partitioning, as well as the pivot selection split out that looks like this:

    
    
            template <class Iter>
            void Sort(Iter b, Iter e)
            {
              const pair<Iter, Iter> midRange = Partition(b, e, SelectPivot(b, e));
              Sort(b, midRange.first);
              Sort(midRange.second, e);
            }
    

You can do the same in C easily enough. So what Haskell had over C/C++ in this
respect was mainly a more expressive standard library. C++ has long had
std::partition(), so it can be done entirely with the standard library too if
you're willing to do the same (bad) fixed/dumb pivot selection that the
typical Haskell versions (as well as the archetypical C version) use.

Unfortunately the link to the CUJ article is dead, as it was a quite
fascinating walk through optimising sorting (including better pivot
selection).

[1]
[http://www.hokstad.com/archives/2005/03/about_haskell_a](http://www.hokstad.com/archives/2005/03/about_haskell_a)

------
M8
_" In order to call a language purely functional, it has to have 6 distinct
features"_

Is there an international standard or a some sort of formal proof?

~~~
gus_massa
The good thing about standards is that each one can choose the one he likes.
(I can't find the exact quote.)

Functional programming is like Object Oriented programming, each one has his
own definition that is very similar to the other definitions but has a few
tweaks to make possible to include his preferred language.

Anyway, I can't understand what this means: (from the article)

> _Static linking: All variables are allocated at compile time and are
> statically linked. No need to go into more details._

Doe it mean no "eval" function?

------
vezzy-fnord
I'm not sure if there's anything inherently imperative about OO, as the author
seems to imply. In addition, I'm not sure just how distanced most FP languages
are from the von Neumann model. I know APL, J and K are categorically non-von
Neumann.

 _Static linking

All variables are allocated at compile time and are statically linked. No need
to go into more details._

I think they meant static binding?

------
dschiptsov
'Functional' means a different way of _thinking_ , not of coding.)

Also Functional != Haskell. Its laziness messed everything up. Functional
subset of Scheme (without set!) is better to grasp the essential ideas (not
cluttered with irrelevant types or cryptic syntax).

------
krat0sprakhar
I'm not sure if this counts as going 'functional' but after having done the FP
Scala course I tried out lodash, and now its the _default_ dependency in all
my web projects :D

------
jcadam
Haskell is fun to play with, but most definitely isn't on the list of
"languages the boss will actually let you use if you ask nicely." The JVM FP
languages like Scala and Clojure are probably more worthwhile to learn, from
that perspective (maybe F# if you're in .NET-land).

I learned Haskell first (man, what a slog that was) to come to grips with FP,
then Scala so that I would actually have the opportunity to use a bit of
functional programming on the job.

Update: Downvotes? Apparently I said something controversial here.

~~~
davexunit
You're being downvoted for making the all too common suggestion that the only
programming languages worthwhile are those that are already widely adopted in
for-profit business. There are better programming languages out there than
what your hypothetical boss will let you use, and they can be used for more
than just "fun".

~~~
jcadam
I do not regret learning Haskell and believe doing so was a worthwhile
exercise. What I'm saying is that the number of jobs out there calling for
Haskell, OCaml, etc. is so small that it is not reasonable to plan on being
able to use such languages professionally.

There are many more programmers that would love to write Haskell for a living
than there are job openings for Haskell developers. I have a _strong_ dislike
for programming in Java. But it pays the mortgage, so feh.

~~~
codygman
I'm using Haskell professionally right now.

------
andronik
It's a me, the author. Let me start.

First of all, I would like to address the 6 distinct features I was talking
about in the theoretical introduction. Those 6 features/rules are described in
the same way in a book called "Introduction to Scheme". I'm guessing I haven't
explained it very well. Basically, what I meant is that those are the rules
functional languages should follow in order to be called pure functional
languages. I didn't say that that is the case with most languages, nor that
this is the case with Haskell or Scheme. I know most of them don't, but that's
just a piece of theory. And yes, I did mean static binding, I apologize, since
linking and binding are denoted by the same word in my language.

Next thing, about the algorithms. Fibonacci example was just to show closeness
to mathematical notation and I know it's probably the worst way to use
recursion because of the O(2^n) time complexity. But, again, complexity was
not the point of the example. About the notation, I'm guessing it's just me
then. I was a lot more comfortable with pure mathematical notation, as I like
things to be formal as much as they can, and that's something that mathematics
provide.

The whole point of the post itself was to give a small introduction to
functional programming and show off features that may not be available in most
imperative languages.

Of course, as I've also said in the outro, I'm not really planning on
switching to purely functional languages, I was just trying to say that they
can be fun in some situations and for some problem solving. The different way
of thinking is what makes it so fun, at least for me.

About higher-order functions, I truly was mistaken, as I've thought that was a
thing for functional languages only. Guess I don't have that much experience
in the imperative world, as I've never came across them in my line of work.

So please, don't take this article as anti-imperative, as it's most certainly
not.

So, tl;dr, I instantly liked functional programming and I wanted to share some
features that I liked the most, and haven't seen in imperative way.

I do apologize for misleading/possibly incorrect parts, and I shall edit those
in my earliest convenience.

------
Nemcue
That is not a good font. Very hard to read on my screen (non-retina).

~~~
Arcanum-XIII
Even on a retina screen : the font need to be bolder since the background is
darker. And a bit small too.

------
tempodox
`reduce` is rather `foldl` than `foldr`.

------
Dewie3
> Few months ago, I came across a new paradigm (to me, at least) called
> functional programming.

You can never go back, at least from the perspective of someone two months
into learning it.

