

From Imperative to Functional: how to make the leap - loup-vaillant
http://loup-vaillant.fr/tutorials/from-imperative-to-functional

======
nkurz
Hi L-V,

I'm a C programmer, and I've gone through the article a couple times without
enlightenment. This isn't necessarily the fault of the article, but thought
I'd offer the places I couldn't follow. Perhaps they will be useful for
understanding a beginners viewpoint.

    
    
      *int square(int n)    │    square :: Int -> Int*
    

Would be great if you offered an example where the argument and return type
differed. As is, I don't know which order the Haskell declaration uses.

    
    
      Now it is possible to write the Haskell program that will
      naturally read from left to write
    

If this is essential at this point, it should be fleshed out more. Otherwise
it feels like a distracting aside and out of place in a short introduction.

    
    
      -- before                        │    -- after
      compute n = baz (bar (foo n))    │    wiz     n = bar (foo n)
                                       │    compute n = baz (wiz n)
    

This seems perfectly clear, which makes me suspect the other parts are
intended to be simple as well.

    
    
      (.) :: (b -> c) -> (a -> b) -> (a -> c)
    

Not clear why the left side is (.). Is this because it's non-alpha? Because
it's infix? Something else? Order to read right hand side still unclear. Also
don't know if reuse of a, b, and c as variables is significant.

    
    
      g . f = λx -> g (f x)
    

Can't quite follow, but this might be because I'm not sure if the λ is
syntactically important, or just a naming convention.

    
    
      be expressed literally (see 1 vs λx -> g (f x));
    

I can't figure out what this means, possibly because I'm not sure what
'literally' means in this context. Also wasn't clear at first that "1" was
meant as a number (or is that an 'L'), not a footnote or reference.

    
    
                           -- A List is either
      data List a = Empty  -- an empty node,  
      | Cons a (List a)    -- or a cons cell
    

Would help to define a "cons cell". Is Cons a data type or a function here? Is
there a standard for capitalization? Not clear to me how the 'a' on the left
side relates to the two on the right. Is 'data' a keyword, a type, or
something else?

    
    
      inc-all l = map (λe -> e + 1) l
    

Throughout this whole section, I had no idea which characters were one's and
which were L's. I ended up cutting and pasting into emacs and capitalizing.
Perhaps something other than 'l' for the placeholder variable? Or it is more
than a placeholder? The concept seemed straightforward, but the example was
very hard to figure out.

    
    
      It is not obvious from the syntax, but Haskell functions      
      only have one argument.
      

Would help to mention this before the (.) example.

    
    
      Multiple arguments are emulated by returning functions.
    

So I guess this means the argument is on the left of the first '->' and the
return type is to the right? And how should '->' be pronounced when spoken?

    
    
      inc-all (Cons e l) = Cons (foo e + 1) (inc-all l)
    

I'm guessing 'e' is 'element', and lower-case 'L' is list. Why/when did we
switch from 'a' in the definition of List? And where did 'foo' come from? Is
this a builtin function? A user defined function?

    
    
      map (a -> b) -> List a -> list b
    

Is the lower case for the last 'list' important? For that matter, is case
important at all in Haskell? Also, are the types on the left for 'a' and 'b'
defined by the given types on the right? Also haven't figured out why this
declaration(?) has no '::' as the others do.

    
    
      dbl-all (Cons e l) = Cons (foo e * e) (inc-all l)
    

Is that supposed be 'dbl-all' at the end? And should that be "2 * e" instead
of "e * e"? I'm hoping these are typos, otherwise I'm understanding even less
than I thought.

    
    
      Note that the first argument is a function, hence the (a -> b) between parentheses.
    

Great! Confirmation on how to read the function declarations. Now back to
reread the first half of the article again. Hopefully this means the return
type is after the last '->'?

    
    
      Very simple, but without the fundamentals I just gave, 
      one hardly stands a chance at deciphering it. 
    

Unfortunately, I still don't feel confident in my ability to decipher now that
I have have the been given those fundamentals. I think a gloss of what 'λe'
actually means would help. As a type, is it parallel to "void * (* e)( void *
)" in C?

Despite this long list, I appreciate that you wrote the article. Much better
to have something possibly flawed than nothing at all. Maybe one day I'll
actually understand it!

~~~
latk
That article was more about declarative/functional programming than about the
details of the Haskell syntax.

Types are capitalized. Lower-case types in signatures are placeholders in
polymorphic functions. The signature

    
    
        (.) :: (b -> c) -> (a -> b) -> (a -> c)
    

means: The function that is the . takes a first argument (g) that maps some
type b to some type c. The second argument (f) maps some type a to the type b.
This ensures that you can always do g(f(x)). The result of the function . is a
function that maps the type a (what f takes) to type c (what g returns).

The type signature only deals with types; this is not connected to the names
of the function parameters.

Lambda expressions are written without types; they can be inferred (i.e. the
return type is not void* , but exactly determined by the compiler). A lambda
expression in lambda calculus notation would be written "λx.(+ x 1)". Haskell
slightly modifies this, and allows infix operators: "\x -> x + 1"(the \ looks
a bit like λ). Punctuation functions are infix by default.

About the data expression: This constructs a new type (typedef). The "|"
operator is just used in a declarative fashion. Every time you write Empty in
your source code, that thing _is_ a list. The expression "list = Cons foo
Empty" would create a list of one element (foo). A cons cell is a pair, this
terminology is popular in Lisp. These confusing data type constructors allow
pattern matching, as you have seen in the definition of "map": If the list is
Empty, return Empty. If the list is a Cons of an element and another list,
then return a Cons of (result of our function applied to that element) and
(the rest of the list mapped) – recursion.

------
mindbat
"Your program isn't about what it does, but about what things are"

This is exactly backwards. OOP programming is about what things are. As Steve
Yegge put it, functional programming is about verbs, about what your program
does to the data passing through it.

Steve's original post, "Execution in the Kingdom of Nouns," is much clearer:
[http://steve-yegge.blogspot.com/2006/03/execution-in-
kingdom...](http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-
nouns.html)

"Your code isn't just a linear sequence of instructions, but a tree of nested
expressions."

This is also off the mark. If you're working in a Lisp, sure, you're directly
manipulating abstract syntax trees. But this is a mark of a homoiconic
language, not a functional one.

"Functions are ordinary mathematical objects, just like an integer is."

Also wrong. In a functional language, functions are _first-class_ objects,
meaning they have an existence independent of classes. But they're something
different from integers, which are usually defined as primitives in the
language.

~~~
loup-vaillant
_Verb vs nouns._ Well, what are objects in OO? They're things that do this,
and change that… This is a lot about what objects do, and what we do to them.
On the other hand, in FP, one "does" only one thing: create new values. There
is a lot of verbs in FP, but they don't _do_ anything… Anyway, I guess I'll
need to review that.

 _Instruction sequence vs Expression tree_ It's jot just Lisp. Ocaml and
Haskell apply as well. We don't _manipulate_ the syntax tree, but it doesn't
mean it isn't there. For instance, the following is a valid Ocaml or Haskell
expression:

    
    
      if condition
      then foo
      else bar (if condition2
                then baz
                else wiz)
    

This is not unusual code. Here is the C equivalent:

    
    
      condition
      ? foo
      : bar(condition2 ? baz : wiz)
    

This _is_ a tree of nested expressions.

 _Functions as ordinary mathematical objects_. I'm just saying that functions
belong to the bucket of mathematical objects. As are integers. And sets. And
lists… "First class" just mean we treat them as such. From
<https://en.wikipedia.org/wiki/First-class_function>

> _In languages with first-class functions, the names of functions do not have
> any special status; they are treated like ordinary variables with a function
> type._

~~~
mindbat
"There is [sic] a lot of verbs in FP, but they don't _do_ anything"

This is both misleading and false. Functions, by their very nature, "do
something."

Perhaps you're referring to the immutability of data in a functional language?
If so, say so.

"We don't manipulate the syntax tree, but it doesn't mean it isn't there."

A statement so ambiguous as to be meaningless. _All_ compiled languages have a
"syntax tree" lurking in the background. It just so happens that Lisp and its
variants let you manipulate that tree directly, rather than writing in code
that gets converted to that tree at compile time.

Perhaps you're referring to the fact that in Haskell, you often don't control
the order in which code is executed, unlike in an imperative language where
you have explicit control?

"...just saying that functions belong to the bucket of mathematical objects.
As are integers. And sets."

Nope. The quote you use has it right. The names of functions are _variables_ ,
whose value is looked up like any variable. This lets you pass functions as
arguments to other functions, or use functions as the return value for other
functions. Functions are most definitely _not_ just like integers in a
functional language.

~~~
loup-vaillant
I meant _expression_ tree, sorry. I'm not referring to Haskell non-strict
evaluation either. My point stands in Ocaml as well. But yeah, expressions do
relinquish _explicit_ control of the evaluation order, for it doesn't matter
(assuming purity).

On the other points, let me remind you that a function is a subset of the
Cartesian product between its domain and its co-domain. How does _that_ do
anything? How does _that_ isn't as ordinary as a mere set? Really, the only
reason we feel that functions do something is because we generally perform
only one action with them: looking up the result, given a parameter.

I insist because this is precisely this special holy first class status that
makes functions scary. Remember the primal fear you felt when your high school
teacher told you that there is an operator that can _compose functions
together_? Neither do I. But it did make clear at that point that functions
aren't that special. If they were, how could I write "f∘g" just like I would
"x+y"? How could I write "f" alone, without an "(x)" right next to it?

Functions do have their specificities, and they _are_ special in the sense
that they are a tremendously useful, wickedly powerful concept. But they're
still mathematical objects. Making them first class in a programming language
just lift restrictions that were there only because it was easier to
implement.

~~~
mindbat
I see. You're using function in its mathematical sense, not in terms of
programming.

Perhaps the article would be clearer if you stated up front that you're trying
to talk about the mathematical underpinnings of the functional programming
paradigm, and _not_ how imperative and functional programming languages differ
in practice? From a practical programming standpoint, your characterization of
functional programming is both incorrect and confusing.

------
friendly_chap
"Your program isn't about what it does, but about what things are"

"In the Haskell code, the data flows from right to left, instead of from top
to bottom"

I find this article vague, imprecise, forced. It's like those "lets do OOP for
the sake of doing OOP" articles, just swap OOP to functional programming.

~~~
loup-vaillant
The statements you cite are trivial, but I believe they are quite precise. Can
you tell me more about how my article spurs a feeling of vagueness? I might be
able to fix it.

Also, I didn't mean to _advocate_ FP, at least not there. I have written it
for the poor C++/Java programmer who is forced to decipher an OCaml script
written by a jerk who since left the company. (Disclaimer: I would be the
jerk.)

~~~
friendly_chap
> Can you tell me more about how my article spurs a feeling of vagueness?

Not just you, approx. 90% of the software industry suffers from it (big IMHO
here of course). I think too much emphasis is placed on paradigms and magic
approaches, instead of talking about things are.

Something like this:

"Is C++ a big pile of mess? Yes.

Is Haskell a much better designed language, even if its implementations are
slower because the higher level of abstraction clashes with the underlying
hardware currently in use? Yep."

Also, what you are trying to describe with all those top-bottom-left-right and
similar analogies is the fact that in FP languages you mostly work with
expressions, while the building blocks of imperative languages are statements.

I don't really have the time to continue here maybe I will write a blog post
myself too :).

------
russellallen
We used to see these articles for object orientation. Now we see them for
functional. I look forward with bated breath for the next paradigm!

~~~
pjmlp
Actually you could already start to see that with multi-paradigm languages.

------
preavy
I have a possible correction. Might dbl-all not be better named sqr-all?
Thanks for the article by the way.

~~~
friendly_chap
Good catch!

------
grapjas
Why do you use 'λ' instead of '\'? Is this normal in haskell projects?

~~~
dkhenry
Either the author has a shiny new keyboard with a λ key, or they really really
like to copy and paste.

~~~
friendly_chap
Syntax error: Either data constructor not in scope.

Perhaps you meant one of these:

    
    
       Left "the author has a shiny new keyboard with a λ key"
    
       Right "they really really like to copy and paste"

------
rosche
Again! I've been hearing the "switch to functional!" spiel since the 70s
(being honest, since sometime in the 60s, but I digress)... stop it already!
Hasn't happened in 40 years, won't happen now.

~~~
tikhonj
It's not like _everyone_ is going to switch, but some people--especially on HN
--might, so it's definitely worth reading about. I certainly know some
startups considering functional programming, largely because so many people
seem to like it.

Also, if you look at other popular technology like OOP, they were pushed even
harder than functional programming. When I was initially learning programming,
I read a ton of books and articles all advocating it. Similarly, I've seen
more people advocating Python than Haskell, and that worked too.

So maybe it's not enough by itself to make seething popular, but it seems
necessary or at least very helpful. There's certainly no reason to stop
trying!

