94

If functional programming languages cannot save any state, how do they do simple stuff like reading input from a user? How do they "store" the input (or store any data for that matter?)

For example: how would this simple C thing translate to a functional programming language like Haskell?

#include<stdio.h>
int main() {
    int no;
    scanf("%d",&no);
    return 0;
}

(My question was inspired by this excellent post: "Execution in the Kingdom of Nouns". Reading it gave me some better understanding of what exactly object oriented programming is, how Java implements it in one extreme manner, and how functional programming languages are a contrast.)

Lazer
  • 90,700
  • 113
  • 281
  • 364
  • possible duplicate of http://stackoverflow.com/questions/1916692/are-side-effects-possible-in-pure-functional-programming – Chris Conway May 01 '10 at 20:16
  • 4
    It's a good question, because on a systemic level a computer needs state to be useful. I watched an interview with Simon Peyton-Jones (one of the devs behind Haskell) where he said that a computer that only ever ran entirely stateless software could only accomplish one thing: Become hot! Many good answers below. There are two main strategies: 1) Make an impure language. 2) Make a cunning plan to abstract state, which is what Haskell does, essentially making a new, slightly changed World instead of modifying the old one. – harms May 01 '10 at 21:07
  • 14
    Wasn't SPJ talking about side effects there, not state? Pure computations have plenty of state implicit in argument bindings and the call stack, but without side effects (e.g., I/O) can't do anything useful. The two points are really quite distinct--there's tons of pure, stateful Haskell code, and the `State` monad is very elegant; on the other hand `IO` is a ugly, dirty hack used only grudgingly. – C. A. McCann May 01 '10 at 21:45
  • 4
    camccann has it right. There's plenty of state in functional languages. It's just explicitly managed instead of "spooky action at a distance" like in imperative languages. – JUST MY correct OPINION May 02 '10 at 05:02
  • Granted, but a computer also needs side-effects to be useful, so the point remains. Sorry for the imprecise terminology in my previous comment. – harms May 02 '10 at 14:19
  • 1
    There may be some confusion here. Perhaps computers need effects to be useful, but I think the question here is about programming languages, not computers. – Conal Dec 18 '10 at 21:10
  • Related: https://stackoverflow.com/q/1020653 . – atravers Oct 18 '21 at 00:30

10 Answers10

81

If functional programming languages cannot save any state, how do they do some simple stuff like reading input from a user (I mean how do they "store" it), or storing any data for that matter?

As you gathered, functional programming doesn't have state—but that doesn't mean it can't store data. The difference is that if I write a (Haskell) statement along the lines of

let x = func value 3.14 20 "random"
in ...

I am guaranteed that the value of x is always the same in the ...: nothing can possibly change it. Similarly, if I have a function f :: String -> Integer (a function taking a string and returning an integer), I can be sure that f will not modify its argument, or change any global variables, or write data to a file, and so on. As sepp2k said in a comment above, this non-mutability is really helpful for reasoning about programs: you write functions which fold, spindle, and mutilate your data, returning new copies so you can chain them together, and you can be sure that none of those function calls can do anything "harmful". You know that x is always x, and you don't have to worry that somebody wrote x := foo bar somewhere in between the declaration of x and its use, because that's impossible.

Now, what if I want to read input from a user? As KennyTM said, the idea is that an impure function is a pure function that's passed the entire world as an argument, and returns both its result and the world. Of course, you don't want to actually do this: for one thing, it's horribly clunky, and for another, what happens if I reuse the same world object? So this gets abstracted somehow. Haskell handles it with the IO type:

main :: IO ()
main = do str <- getLine
          let no = fst . head $ reads str :: Integer
          ...

This tells us that main is an IO action which returns nothing; executing this action is what it means to run a Haskell program. The rule is that IO types can never escape an IO action; in this context, we introduce that action using do. Thus, getLine returns an IO String, which can be thought of in two ways: first, as an action which, when run, produces a string; second, as a string that's "tainted" by IO since it was obtained impurely. The first is more correct, but the second can be more helpful. The <- takes the String out of the IO String and stores it in str—but since we're in an IO action, we'll have to wrap it back up, so it can't "escape". The next line attempts to read an integer (reads) and grabs the first successful match (fst . head); this is all pure (no IO), so we give it a name with let no = .... We can then use both no and str in the .... We've thus stored impure data (from getLine into str) and pure data (let no = ...).

This mechanism for working with IO is very powerful: it lets you separate the pure, algorithmic part of your program from the impure, user-interaction side, and enforce this at the type level. Your minimumSpanningTree function can't possibly change something somewhere else in your code, or write a message to your user, and so on. It's safe.

This is all you need to know to use IO in Haskell; if that's all you want, you can stop here. But if you want to understand why that works, keep reading. (And note that this stuff will be specific to Haskell—other languages may choose a different implementation.)

So this probably seemed like a bit of a cheat, somehow adding impurity to pure Haskell. But it isn't—it turns out that we can implement the IO type entirely within pure Haskell (as long as we're given the RealWorld). The idea is this: an IO action IO type is the same as a function RealWorld -> (type, RealWorld), which takes the real world and returns both an object of type type and the modified RealWorld. We then define a couple functions so we can use this type without going insane:

return :: a -> IO a
return a = \rw -> (a,rw)

(>>=) :: IO a -> (a -> IO b) -> IO b
ioa >>= fn = \rw -> let (a,rw') = ioa rw in fn a rw'

The first one allows us to talk about IO actions which don't do anything: return 3 is an IO action which doesn't query the real world and just returns 3. The >>= operator, pronounced "bind", allow us to run IO actions. It extracts the value from the IO action, passes it and the real world through the function, and returns the resulting IO action. Note that >>= enforces our rule that the results of IO actions never be allowed to escape.

We can then turn the above main into the following ordinary set of function applications:

main = getLine >>= \str -> let no = (fst . head $ reads str :: Integer) in ...

The Haskell runtime jump-starts main with the initial RealWorld, and we're set! Everything's pure, it just has a fancy syntax.

[Edit: As @Conal points out, this is not actually what Haskell uses to do IO. This model breaks if you add concurrency, or indeed any way for the world to change in the middle of an IO action, so it would be impossible for Haskell to use this model. It is accurate only for sequential computation. Thus, it may be that Haskell's IO is a bit of a dodge; even if it isn't, it's certainly not quite this elegant. Per @Conal's observation, see what Simon Peyton-Jones says in Tackling the Awkward Squad [pdf], section 3.1; he presents what might amount to an alternative model along these lines, but then drops it for its complexity and takes a different tack.]

Again, this explains (pretty much) how IO, and mutability in general, works in Haskell; if this is all you want to know, you can stop reading here. If you want one last dose of theory, keep reading—but remember, at this point, we've gone really far afield from your question!

So the one last thing: it turns out this structure—a parametric type with return and >>=— is very general; it's called a monad, and the do notation, return, and >>= work with any of them. As you saw here, monads aren't magical; all that's magical is that do blocks turn into function calls. The RealWorld type is the only place we see any magic. Types like [], the list constructor, are also monads, and they have nothing to do with impure code.

You now know (almost) everything about the concept of a monad (except a few laws that must be satisfied and the formal mathematical definition), but you lack the intuition. There are a ridiculous number of monad tutorials online; I like this one, but you have options. However, this probably won't help you; the only real way to get the intuition is via a combination of using them and reading a couple tutorials at the right time.

However, you don't need that intuition to understand IO. Understanding monads in full generality is icing on the cake, but you can use IO right now. You could use it after I showed you the first main function. You can even treat IO code as though it was in an impure language! But remember that there's an underlying functional representation: nobody's cheating.

(PS: Sorry about the length. I went a little far afield.)

Community
  • 1
  • 1
Antal Spector-Zabusky
  • 36,191
  • 7
  • 77
  • 140
  • 6
    The thing that always gets me about Haskell (which I have made, and am making valiant efforts to learn) is the ugliness of the syntax. It's like they took the worst bits of every other language, dropped them in a bucket and stirred furiously. And these people will then complain about C++'s admittedly (in places) weird syntax! –  May 01 '10 at 20:48
  • 20
    Neil: Really? I actually find Haskell's syntax very clean. I'm curious; what in particular are you referring to? (For what it's worth, C++ doesn't really bother me either, except for the need to do `> >` in templates.) – Antal Spector-Zabusky May 01 '10 at 20:57
  • 2
    @absz I suppose I'm comparing it with C as a procedural language (pointer declarations the most obvious failure), Smalltalk as an OO language (precedence the big problem) and Scheme as a functional language (usual moans about lisp syntax), But in all these I can when looking at a piece of code get a good idea of what it is doing (and I am far, far from being either an ST or Scheme expert). I simply cannot do that with Haskell. It seems like the tokens of the language were chosen more or less at random. Well, maybe it will all become clear - I hope so.l –  May 01 '10 at 21:19
  • 6
    To my eye, while Haskell's syntax isn't as clean as, say, Scheme, it doesn't begin to compare to the hideous syntax of, well, even the nicest of the curly-brace languages, of which C++ is among the worst. No accounting for taste, I suppose. I don't think a language exists that everyone finds syntactically pleasing. – C. A. McCann May 01 '10 at 21:23
  • 8
    @NeilButterworth: I suspect your problem isn't so much the syntax as the function names. If functions like `>>=` or `$` had more where instead called `bind` and `apply`, haskell code would look a lot less like perl. I mean the main difference between haskell and scheme syntax is that haskell has infix operators and optional parens. If people would refrain from overusing infix operators, haskell would look a lot like scheme with less parens. – sepp2k May 01 '10 at 21:43
  • 1
    @sepp2k: I think I know what you're getting at, but "infix operators and optional parens"? Without parens and prefix notation, Scheme barely has any syntax left! It's like saying "well, the main difference between a lamppost and an apple tree is that the lamppost isn't a tree and doesn't grow apples". – C. A. McCann May 01 '10 at 21:55
  • 5
    @camcann: Well, point, but what I meant is: The basic syntax of scheme is `(functionName arg1 arg2)`. If you remove the parens it's `functionName arg1 arg2` which is haskell syntax. If you allow infix operators with arbitrarily horrible names you get `arg1 §$%&/*°^? arg2` which is even more like haskell. (I'm just teasing btw, I actually like haskell). – sepp2k May 01 '10 at 22:22
  • 3
    @absz: I describe in another answer how this explanation of `IO` (as `RealWorld -> (type, RealWorld)`), while popular & persistent, is not true of Haskell. The explanation does not account for concurrency. And "concurrency" even in a very weak sense, including the world itself changing during an IO computation for reasons other than the computation itself. – Conal May 04 '10 at 06:43
  • 1
    @Conal: I saw that answer, and you are absolutely correct. I'd never thought about that aspect of `IO` before. – Antal Spector-Zabusky May 04 '10 at 15:23
23

Plenty of good answers here, but they are long. I'm going to try to give a helpful short answer:

  • Functional languages put state in the same places that C does: in named variables and in objects allocated on the heap. The differences are that:

    • In a functional language, a "variable" gets its initial value when it comes into scope (through a function call or let-binding), and that value doesn't change afterward. Similarly, an object allocated on the heap is immediately initialized with the values of all its fields, which don't change thereafter.

    • "Changes of state" handled not by mutating existing variables or objects but by binding new variables or allocating new objects.

  • IO works by a trick. A side-effecting computation that produces a string is described by a function that takes a World as argument, and returns a pair containing the string and a new World. The World includes the contents of all disk drives, the history of every network packet ever sent or received, the color of each pixel on the screen, and stuff like that. The key to the trick is that access to the World is carefully restricted so that

    • No program can make a copy of the World (where would you put it?)

    • No program can throw away the World

    Using this trick makes it possible for there to be one, unique World whose state evolves over time. The language run-time system, which is not written in a functional language, implement a side-effecting computation by updating the unique World in place instead of returning a new one.

    This trick is beautifully explained by Simon Peyton Jones and Phil Wadler in their landmark paper "Imperative Functional Programming".

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • 4
    As far as I can tell, this `IO` story (`World -> (a,World)`) is a myth when applied to Haskell, as that model explains only purely sequential computation, while Haskell's `IO` type includes concurrency. By "purely sequential", I mean that not even the world (universe) is allowed to change between the start and end of an imperative computation, other than due to that computation. For instance, while your computer is chugging away, your brain etc cannot. Concurrency can be handled by something more like `World -> PowerSet [(a,World)]`, which allows for nondeterminism and interleaving. – Conal May 01 '10 at 22:37
  • 1
    @Conal: I think the IO story generalizes pretty nicely to nondeterminism and interleaving; if I remember right, there's a pretty good explanation in the "Awkward Squad" paper. But I don't know of a good paper that explains true parallelism clearly. – Norman Ramsey May 01 '10 at 23:01
  • 3
    As I understand it, the "Awkward Squad" paper abandons the attempt to generalize the simple denotational model of `IO`, i.e. `World -> (a,World)` (the popular & persistent "myth" I referred to) and instead gives an operational explanation. Some folks like operational semantics, but they leave me thoroughly dissatisfied. Please see my longer reply in another answer. – Conal May 02 '10 at 17:19
  • +1 This has helped me understand IO Monads a lot more as well as answering the question. – CaptainCasey May 04 '10 at 01:20
  • Most Haskell compilers actually do define `IO` as `RealWorld -> (a,RealWorld)`, but instead of actually representing the real world it's just an abstract value that has to get passed around and ends up getting optimized out by the compiler. – Jeremy List Apr 24 '14 at 13:33
  • Norman: not only is it a trick, it can also be tricky - I show how in https://stackoverflow.com/a/64768590 . – atravers Feb 05 '21 at 23:31
20

I'm breaking off a comment reply to a new answer, to give more space:

I wrote:

As far as I can tell, this IO story (World -> (a,World)) is a myth when applied to Haskell, as that model explains only purely sequential computation, while Haskell's IO type includes concurrency. By "purely sequential", I mean that not even the world (universe) is allowed to change between the start and end of an imperative computation, other than due to that computation. For instance, while your computer is chugging away, your brain etc cannot. Concurrency can be handled by something more like World -> PowerSet [(a,World)], which allows for nondeterminism and interleaving.

Norman wrote:

@Conal: I think the IO story generalizes pretty nicely to nondeterminism and interleaving; if I remember right, there's a pretty good explanation in the "Awkward Squad" paper. But I don't know of a good paper that explains true parallelism clearly.

@Norman: Generalizes in what sense? I'm suggesting that the denotational model/explanation usually given, World -> (a,World), doesn't match Haskell IO because it doesn't account for nondeterminism and concurrency. There might be a more complex model that does fit, such as World -> PowerSet [(a,World)], but I don't know whether such a model has been worked out and shown adequate & consistent. I personally doubt such a beast can be found, given that IO is populated by thousands of FFI-imported imperative API calls. And as such, IO is fulfilling its purpose:

Open problem: the IO monad has become Haskell’s sin- bin. (Whenever we don’t understand something, we toss it in the IO monad.)

(From Simon PJ's POPL talk Wearing the hair shirt Wearing the hair shirt: a retrospective on Haskell.)

In Section 3.1 of Tackling the Awkward Squad, Simon points what doesn't work about type IO a = World -> (a, World), including "The approach does not scale well when we add concurrency". He then suggests a possible alternative model, and then abandons the attempt at denotational explanations, saying

However we will instead adopt an operational semantics, based on standard approaches to the semantics of process calculi.

This failure to find a precise & useful denotational model is at the root of why I see Haskell IO as a departure from the spirit and the deep benefits of what we call "functional programming", or what Peter Landin more specifically named "denotative programming". See comments here.

Conal
  • 18,517
  • 2
  • 37
  • 40
  • Thanks for the longer answer. I think perhaps I have been brainwashed by our new operational overlords. Left movers and right movers and so on have made it possible to prove some useful theorems. Have you seen *any* denotational model you like that accounts for nondeterminism and concurrency? I have not. – Norman Ramsey May 03 '10 at 01:41
  • 1
    I like how `World -> PowerSet [World]` crisply captures nondeterminism and interleaving-style concurrency. This domain definition tells me that mainstream concurrent imperative programming (including Haskell's) is intractable--literally exponentially more complex than sequential. The great harm I see in the Haskell `IO` myth is obscuring this inherent complexity, demotivating its overthrow. – Conal May 03 '10 at 19:46
  • While I see why `World -> (a, World)` is broken, I'm not clear on why the replacement `World -> PowerSet [(a,World)]` properly models concurrency, etc. To me, that seems to imply that programs in `IO` should run in something like the list monad, applying themselves to every item of the set returned by the `IO` action. What am I missing? – Antal Spector-Zabusky May 04 '10 at 15:39
  • 3
    @Absz: First, my suggested model, `World -> PowerSet [(a,World)]` isn't right. Let's try `World -> PowerSet ([World],a)` instead. `PowerSet` gives the set of possible results (nondeterminism). `[World]` is sequences of intermediate states (not the list/nondeterminism monad), allowing for interleaving (thread scheduling). And `([World],a)` isn't quite right either, as it allows access to `a` before going through all the intermediate states. Instead, define use `World -> PowerSet (Computation a)` where `data Computation a = Result a | Step World (Computation a)` – Conal May 04 '10 at 17:10
  • I still don't see a problem with `World -> (a, World)`. If the `World` type really includes all the world, then it also includes the information about all the processes running concurrently, and also the 'random seed' of all non-determinism. The resulting `World` is a world with time advanced and some interaction performed. The only real problem with this model seems to be that it is too general and the values of `World` can not be constructed and manipulated. – Rotsor Aug 16 '11 at 02:05
  • @Rotsor: Great. Now in that model, imagine how to define the semantics of a useful concurrency mechanism, such as `forkIO`. Or even interaction, which is a sort of concurrency between program and the outside world. After you've tried on your own, see section 3.1 of [the "awkward squad" paper](http://research.microsoft.com/en-us/um/people/simonpj/papers/marktoberdorf/) (on denotational semantics), which describes some problems with `World -> (a, World)` and gives some halfhearted attempts at alternatives before abandoning the denotational enterprise in favor of operational. – Conal Aug 18 '11 at 00:42
  • I see how it's broken now. The convincing argument from the paper was the impossibility of differentiating between different kinds of infinite looping (printing forever vs doing nothing forever) using `World -> (a, World)` approach. I still don't see why concurrency is relevant though. `forkIO` is just a function returning a world having an additional thread running. – Rotsor Aug 19 '11 at 18:30
  • The tricky bit is getting parallel computations to interact/interfere with each other, considering that the attempted model reveals no intermediate states. – Conal Aug 20 '11 at 00:06
  • @Rotsor: The difficulty you mentioned ("printing forever vs doing nothing forever") is just a special case of the concurrency difficulty I mentioned (in this case between program and outside world). The model `World -> (a, World)` doesn't give access to intermediate states, and so the print effects go unseen. Similarly for other forms of concurrency (include thread/thread). – Conal Aug 22 '11 at 21:04
  • I'm not convinced by the argument that "the IO Monad is World -> (a,World)" couldn't be true of Haskell. Mercury **explicitly** uses state-of-the-world passing to model IO (using unique values and an abstract type to prevent you reusing one or creating a new one). So by your logic Mercury's IO system cannot work. A section of this PHD thesis describes the way this is viewed as working when concurrency is involved (which it always is even for single-threaded programs, as you point out): http://www.mercury.csse.unimelb.edu.au/information/papers.html#conway-thesis – Ben Feb 18 '12 at 02:39
  • Basically all user-level IO computations pass values of `io` type around without performing any operations on them directly, because no such operations are exposed. At some point a "real" IO computation is evaluated, it maps the world it was given (which was ultimately provided by the last "real" IO computation) to one which the effects of this action **and arbitrary other effects**. Since the only way concurrent effects can affect a program is by affecting the results returned by IO computation, this model works fine, doesn't it? – Ben Feb 18 '12 at 03:01
  • @Ben There's provably nothing wrong with the world-passing implementation of the IO monad as long as the compiler doesn't duplicate redexes, which is something no-one in their right mind wants a compiler to do anyway. – Jeremy List Apr 24 '14 at 14:49
  • What do you think of Edward Kmett's [approach](http://comonad.com/reader/2011/free-monads-for-less-3/) of just punting on the question of what IO is really about, leaving the question of what a primitive action is to Someone Else? – dfeuer Sep 18 '15 at 05:25
17

Functional programing derives from lambda Calculus. If you truly want to understand Functional programing check out http://worrydream.com/AlligatorEggs/

It is a "fun" way to learn lambda Calculus and bring you into the exciting world of Functional programming!

How knowing Lambda Calculus is helpful in functional programming.

So Lambda Calculus is the foundation for many real-world programming languages such as Lisp, Scheme, ML, Haskell,....

Suppose we want to describe a function that adds three to any input to do so we would write:

plus3 x = succ(succ(succ x)) 

Read “plus3 is a function which, when applied to any number x, yields the successor of the successor of the successor of x”

Note that the function which adds 3 to any number need not be named plus3; the name “plus3” is just a convenient shorthand for naming this function

(plus3 x) (succ 0) ≡ ((λ x. (succ (succ (succ x)))) (succ 0))

Notice we use the lambda symbol for a function (I think it looks kind of like an Alligator I'm guessing thats where the idea for Alligator eggs came from)

The lambda symbol is the Alligator (a function) and the x is its color. You can also think of x as an argument (Lambda Calculus functions are really only suppose to have one argument) the rest you can think of it as the body of the function.

Now consider the abstraction:

g ≡ λ f. (f (f (succ 0)))

The argument f is used in a function position (in a call). We call g a higher-order function because it takes another function as an input. You can think of the other function calls f as "eggs". Now taking the two functions or "Alligators" we have created we can do something like this:

(g plus3) = (λ f. (f (f (succ 0)))(λ x . (succ (succ (succ x)))) 
= ((λ x. (succ (succ (succ x)))((λ x. (succ (succ (succ x)))) (succ 0)))
 = ((λ x. (succ (succ (succ x)))) (succ (succ (succ (succ 0)))))
 = (succ (succ (succ (succ (succ (succ (succ 0)))))))

If you notice you can see that our λ f Alligator eats our λ x Alligator and then the λ x Alligator and dies. Then our λ x Alligator is reborn in the λ f's Alligator eggs. Then the process repeats and the λ x Alligator on the left now eats the other λ x Alligator on the right.

Then you can use this simple set of rules of "Alligators" eating "Alligators" to design a grammar and thus Functional programming languages were born!

So you can see if you know Lambda Calculus you will understand how Functional Languages work.

I. J. Kennedy
  • 24,725
  • 16
  • 62
  • 87
PJT
  • 3,439
  • 5
  • 29
  • 40
  • @tuckster: I have studied lambda calculus a number of times before... and yes AlligatorEggs article makes sense to me. But I am not able to relate that to programming. For me, right now, labda calculus is like a separate theory, that is just there. How are the concepts of lambda calculus used in programming languages? – Lazer May 01 '10 at 20:35
  • 3
    @eSKay: Haskell *is* lambda calculus, with a thin layer of syntactic sugar to make it look more like a normal programming language. Lisp-family languages are also very similar to the untyped lambda calculus, which is what Alligator Eggs represents. Lambda calculus itself is essentially a minimalist programming language, kind of like a "functional programming assembly language". – C. A. McCann May 01 '10 at 21:11
  • @eSKay: I have added a bit about how it relates with some examples. I hope this helps! – PJT May 01 '10 at 23:59
  • If you are going to subtract from my answer could you please leave a comment about why so I can try and improve my answer. Thank you. – PJT May 03 '10 at 21:47
14

The technique for handling state in Haskell is very straightforward. And you don't need to understand monads to get a handle on it.

In a programming language with state, you typically have some value stored somewhere, some code executes, and then you have a new value stored. In imperative languages this state is just somewhere "in the background". In a (pure) functional language you make this explicit, so you explicitly write the function that transforms the state.

So instead of having some state of type X, you write functions that map X to X. That's it! You switch from thinking about state to thinking about what operations you want to perform on the state. You can then chain these functions together and combine them together in various ways to make entire programs. Of course you're not limited to just mapping X to X. You can write functions to take various combinations of data as input and return various combinations at the end.

Monads are one tool, among many, to help organise this. But monads aren't actually the solution to the problem. The solution is to think about state transformations instead of state.

This also works with I/O. In effect what happens is this: instead of getting input from the user with some direct equivalent of scanf, and storing it somewhere, you instead write a function to say what you'd do with the result of scanf if you had it, and then pass that function to the I/O API. That's exactly what >>= does when you use the IO monad in Haskell. So you never need to store the result of any I/O anywhere - you just need to write code that says how you'd like to transform it.

sigfpe
  • 7,996
  • 2
  • 27
  • 48
8

(Some functional languages permit impure functions.)

For purely functional languages, the real world interaction is usually included as one of the function arguments, like this:

RealWorld pureScanf(RealWorld world, const char* format, ...);

Different languages have different strategies to abstract the world away from the programmer. Haskell, for instance, uses monads to hide the world argument.


But the pure part of functional language itself is already Turing complete, meaning anything doable in C is also doable in Haskell. The main difference to imperative language is instead of modifying states in place:

int compute_sum_of_squares (int min, int max) {
  int result = 0;
  for (int i = min; i < max; ++ i)
     result += i * i;  // modify "result" in place
  return result;
}

You incorporate the modification part into a function call, usually turning loops into recursions:

int compute_sum_of_squares (int min, int max) {
  if (min >= max)
    return 0;
  else
    return min * min + compute_sum_of_squares(min + 1, max);
}
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Or just `computeSumOfSquares min max = sum [x*x | x <- [min..max]]` ;-) – fredoverflow May 02 '10 at 13:54
  • @Fred: List comprehension is just a syntactic sugar (and then you need to explain the List monad in detail). And how do you implement `sum`? Recursion is still needed. – kennytm May 02 '10 at 16:09
3

Functional language can save state! They usually just either encourage or force you to be explicit about doing so.

For example, check out Haskell's State Monad.

Shaun
  • 3,928
  • 22
  • 21
  • 9
    And keep in mind that there's nothing about `State` or `Monad` that enables state, since they are both defined in terms of simple, general, functional tools. They just capture relevant patterns, so you don't have to reinvent the wheel so much. – Conal May 01 '10 at 22:40
3

might be useful , Function Programming for the rest of us

Ahmed Kotb
  • 6,269
  • 6
  • 33
  • 52
1

haskell:

main = do no <- readLn
          print (no + 1)

You can of course assign things to variables in functional languages. You just can't change them (so basically all variables are constants in functional languages).

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • @sepp2k: why, whats the harm in changing them? – Lazer May 01 '10 at 19:39
  • @eSKay if you can't change the variables then you know that they are allways the same. This makes it easier to debug, forces you to make simpler functions that do one thing only and very well. It also helps a great deal when working with concurrency. – Henrik Hansen May 01 '10 at 19:48
  • 9
    @eSKay: Functional programmers believe that mutable state introduces many possibility for bugs and makes it harder to reason about the behavior of programs. For example if you have a function call `f(x)` and you want to see what the value of x is, you just have to go to the place where x is defined. If x were mutable you'd also have to consider whether there's any point where x could be changed between its definition and its use (which is non-trivial if x is not a local variable). – sepp2k May 01 '10 at 19:48
  • 6
    It isn't just functional programmers that distrust mutable state and side effects. Immutable objects and command/query separation are well-regarded by quite a few OO programmers, and almost *everyone* thinks mutable global variables are a bad idea. Languages like Haskell just take the idea further that most... – C. A. McCann May 01 '10 at 21:49
  • 5
    @eSKay: It's not so much that mutation is harmful is that it turns out if you agree to avoid mutation it becomes much easier to write code that is modular and reusable. Without shared mutable state, coupling between different parts of the code becomes explicit and it is a lot easier to understand and maintain your design. John Hughes explains this better than I can; grab his paper *Why Functional Programming Matters*. – Norman Ramsey May 02 '10 at 01:23
0

If functional programming languages cannot save any state, how do they do simple stuff like reading input from a user [for later use]?

The language might not, but its implementation certainly does! Think of all the state in there - at least one stack, one or more heaps, various file descriptors, the current configuration, and so forth. Thankfully, it's the computer which is dealing with all that, instead of you. Hmm - letting the computer deal with the boring bits: what a concept!

At this rate, implementations will be taking on all that dreary I/O activity any day now - then you'll be hearing about denotative languages...yes, more jargon for the newbies! But for now, we'll focus on what already exists - functional languages: how do they do simple I/O stuff like e.g. reading input?

Very carefully!

What makes most functional languages different from imperative languages is that only the direct manipulation of state for I/O is allowed - you cannot define some extra state anonymously inside a definition to e.g. record the number of times it was used. To prevent this from happening, types are often used to distinguish between I/O-based and I/O-free code, with Haskell and Clean making extensive use of the technique.

This can work well enough to even give functional languages the ability to call subroutines procedures in imperative languages via a so-called "foreign function interface". This allow a veritable infinitude of I/O-centric operations (and the consequent manipulation of I/O-based state) to be introduced into the functional language - scanf() is just the beginning...


...wait a moment: "a veritable infinitude of I/O-centric operations"? A finite implementation cannot possibly hold all that, so a totally-denotative language will always be limited in some way with regards to the outside interactions of its programs. Therefore I/O must always be a part of any general-purpose programming language.

atravers
  • 455
  • 4
  • 8