All my methods take 316 arguments, and I like it that way
muckandbrass.com
muckandbrass.com
All functional languages have mutable global variables. Most provide explicit support for mutable values (e.g., Scheme, OCaml), but even in the "pure" ones, there are the secretly global mutable values. Clojure gets them from its STM and from classes in the JVM. Erlang stashes them in ETS, DETS, and Mnesia. Haskell shoves them into the IO and State monads (among others) and hopes you won't notice. In all of these cases, the variable changes may be isolated, threadsafe, or internally consistent, or however you wish to describe it, but if that's not mutable state at a global level, I don't know what on Earth you'd call it. In all cases, this means that a function called twice in a row from the same point in a program may return two different values--and that's exactly what the author is saying is a flaw of imperative languages.
Turning things around, while it's theoretically possible that any given function mutates global state in random ways, no good program is written that way. Instead, the relevant state is passed to a function in the form of a struct or class--exactly what you'd be doing in a functional language, as it happens. The only difference is that the function can, and sometimes does, mutate the structure passed to it, whereas the functional equivalent would likely return a slightly different version of that struct. But, again, a combination of passing things conservatively, naming functions well, and making heavy use of const/final/sealed/what have you to lock things down makes this problem largely absent in practice.
I am not saying that functional programming can't a huge improvement for some things. I am utterly convinced that a multithreaded program should be written in a functional way, if not in a purely functional language. I likewise tremendously favor functional languages for algorithm-heavy applications, where the ease and safety with which I can backtrack and memoize can yield huge productivity gains. But saying that all imperative programs have hundreds of arguments, while functional languages have only those defined? Unless you do no output with the outside world, that claim is simply ludicrous.
Who said anything about mutating global state in random ways? All you need is to have some state mutated in some way that you aren't aware of to make life hell in an imperative environment. Considering that that happens all the time, especially given the modern conveniences of large frameworks and libraries...
> But, again, a combination of passing things conservatively, > naming functions well, and making heavy use of > const/final/sealed/what have you to lock things down makes > this problem largely absent in practice.
This is flatly wrong, and equivalent to saying that you can write concurrent code in an imperative environment without a problem as long as you use locks just right.
A global variable is a variable that is accessible and modifiable at all times in all parts of the program with no particular access control, since the idea of "access control" wasn't even around at the time. C, Python, and Perl all have global variables. Despite your contention, Erlang and Haskell do not.
Mnesia, ETS, and DETS all control access via message passing; it's a common misconception but you never get direct access to an Mnesia table and with ETS I think you get a choice of "direct access in one process only" or "ETS spawns a process to manage the table" (not sure about DETS, but I guarantee it still won't be global). How could you even get direct access to a table that may be on another physical server? Mnesia has transactions for Pete's sake; how much less "no particular access control" can you get?! The closest thing to global variables you can get in Erlang is the process dictionary, which is still process-local. Erlang is probably the hardest language I know to have a true global in, because even if you cheat and have a C extension that has a C global in it, odds are that Erlang will still access it via an Erlang process wrapped around it which still has the effect of de facto locking the value into atomic reads and writes.
Haskell's State monad does not have anything remotely resembling a "global" value; it is explicitly scoped, labelled by the type system so that you can't cheat your way into it, and it literally desugars into threading functional values through a series of function calls. If that's a global variable, then you've devalued the term "global variable" to the point where every parameter of every function call is a global variable. The IO monad can contain a global value by the definition I gave above, if and only if you use "unsafePerformIO", which is an escape hatch that loudly proclaims "I AM LEAVING PURE FUNCTIONAL BEHIND AND I AM ASKING FOR THE RESULTING PAIN", and, indeed, leaves pure functional behind and asks for the resulting pain. So while that does allow you a global variable, if you really push it, it will actually end up very hard to use due to the difficult-to-predict order of evaluation and isn't a useful thing to do with unsafePerformIO. Otherwise, you only get access to the variable in IO-labeled functions, which except in pathological programs won't be anything like "all functions" and is certainly not globally available to everything. And you'll still need to do some major-league cheating to get access to even the "global" variables I mention here without passing explicit references to them all around; I'm still not sure that's possible without a C extension though I don't know enough to rule it out completely.
I can't argue for the other languages you mention since I don't know them.
Your argument is flawed, since you have some sort of weirdly potent definition of "global variable" that results in numerous things that aren't "global" getting called "global".
ets:new(global_state, [set, public, named_table])
Now we've got a named table that any process may read or write to. It is possible.You have to be careful to think about this clearly. The defining characteristic of a true global is not that it can be obtained in some manner from everywhere in the program; by that standard even in Haskell everything's global, as there exists some organization of the program that would permit access of a given value from everywhere (though it would be one hell of a bastardized program). The characteristics are that everybody has direct, simultaneous access. If "ets" tables qualify as "global variables", then so do values sitting on a MySQL server; in erlang the difference between a MySQL value and an ETS value is pretty minimal, you access both via message passing. This is not a useful definition of "global value".
Now, it may still turn out that even with this level of access, it is "too free" and still subject to a reduced amount of the pain that global variables can bring. Haskell is a constructive example of a language that implicitly makes that argument and further distinguishes this case. But we don't benefit from failing to discriminate between a managed value that the manager permits anything to access the gate (albeit still in a managed fashion), and a completely unmanaged global value. They do have some similarities, but nevertheless, managed variables are nowhere near as bad as truly global variables.
If you're not careful and you come at this with fuzzy enough thinking, you'll end up spinning yourself into a tizzy where everything's the same. In fact what the situation is is that there's tens or hundreds of ever-so-slightly different cases and terms that try to cross the language barriers often have some issue, but there's still some broad patterns we can find, and if you find yourself just smooshing them all together you're going to find it hard to pick the right tool for the right job.
Because of that you can write this code (to piggyback on the example you are responding to):
foo(Key) -> ets:member(global_state, Key).
Instead of:
foo(Tid, Key) -> ets:member(Tid, Key).
In the second case, all dependencies are explicit, in the first case, there is a dependency on the atom global_state being the name of the table you are accessing.
FP is letting us get control of our state in ways undreamt of previously. Unfortunately, we're still not really good at it.
For Haskell, this is false. In order to get two different results from the invocation of the same function you must pass in two different arguments. The monadic functions work by yielding a new version of the world, as it were.
Here's a nice explanation of Haskell IO that makes sense to me: http://www.haskell.org/haskellwiki/IO_inside
http://blog.sigfpe.com/2006/08/you-could-have-invented-monad...
In both of these cases, if you don't notice, your program won't compile anymore. So really, your statement is the opposite of the truth. Haskell hopes you do notice.
It's entirely possible and very natural to write imperative programs that don't rely on every possible global variable your application has ever declared.
True, if you're writing a library for consumption by others, then you would have to make it clear that your function depends on certain global variables being defined. But I would like to believe most people writing APIs for use by other programmers would know better than to rely on this type of behavior.
There is however a nice property that Haskell makes it explicit in the type signature if global mutable state is available to the function. This makes it a lot easier to guarantee that large parts of a program is indeed side-effect free.
http://prog21.dadgum.com/54.html
That article's main point was that functional programming doesn't work because deep in the bowels of two otherwise-pure leaf functions, you might want to make their behavior interdependent. In an imperative language, you can do this through adding a simple global variable. In a functional language, you have to change the signatures of all the callers to add this new parameter.
A lot of people pointed out that in a real program, you're asking for massive maintenance headaches if you connect two independent leaf functions through a global variable and make their behavior interdependent. In that context, this isn't a strawman, because somebody actually did suggest structuring programs this way.
Make a silly suggestion, get a silly reply.
I think both authors would do well to focus less on individual languages or programming paradigms and more on writing actual large-scale software systems. Because the imperative, OO, and functional worlds basically converge on that point: don't do that. You don't want the behavior of apparently-pure functions to change based on hidden, undocumented state. And once you document it, you might as well add it to the function signature, which is your "executable documentation" anyway.
In languages like Haskell, the type system ensures that there are functions whose effects can be understood by only reading that one function.
The usual imperative languages don't even make safety an option. You just have to try hard and hope for the best.
Isn't the same true for, say, garbage collection? Higher level languages are often defined by the things they don't let you do, compared to lower level languages, in order to make your life easier.
Of course, if you're a C++ programmer, you probably agree that both mandatory garbage collection and immutable state are equally nanny-language features.
Ugh, no. C programmer. I don't have an intrinsic problem with GC (as it allows me to be lazy), though admittedly there are quite a few bad GC implementations out there that can cause noticeable performance issues.
Immutable state has its uses, but, again, it's a question of whether or not it's mandatory.
So while driving a boat is fun, keep that car nearby.
Now, somehow the original point (http://prog21.dadgum.com/54.html) has missed its mark. The original point was this: that for certain kinds of interactions, an imperative solution with shared state solution is vastly less complicated and preferable to a purely functional solution. This is because in some cases the purely function solution will litter a whole hierarchy of function calls or an entire set of data structures with additional data, but the imperative, stateful solution will not.
Now, obviously the problem with imperative is:
"...that state gets threaded everywhere, and you can't look at any function individually and know how it will behave."
But what is the problem with functional programming? That you can't look at any bit of state in the program unless some other part of the program has explicitly given it to you.
Now, I'd say that 95% of the time I'd rather have that problem than the problems that come with state - like having to worry about call order semantics, reentrancy, etc etc. But dammit - 5% of the time I'd rather use the state and be done with it!
And more generally speaking, 100% of the time I'd rather have a concrete example of something that is implemented simply today than an abstract idea of why something might be complicated in the future.
That's not true in general, but the problematic truth is that you don't know when it is true and when it is not true in any particular situation. That is, there are no formal guarantees that tell the caller of a function that the function does not depend on each and every mutable state in the program.
The second issue is the fact that methods in object oriented code tend to depend on too much state held in the instance variables of their containing class. This is mostly due to bad design. But there must be something else if even the best programmers out there feel the need to make this design mistake over and over again when using OO languages.
I am sympathetic to functional programming but for some of the code I write I have to carefully optimize the memory layout of my data structures. Adding an immutability requirement to very stringent memory usage and performance requirements is just not feasible in these situations.
> Of course, there is a place for mutable, imperative programming. The fellow who wrote the blog post to which I linked above appears to work on games, one of the few places where one could unapologetically use an imperative programming language with mutable state.
Comtrast http://lambda-the-ultimate.org/node/1277 where the authors of Unreal point out that functional programs will be the future of game programming.
"The behaviour of every function in a mutable, imperative environment is dependent upon the state of all of the other (variables|attributes|bindings|whatever) in your program at the time the function is invoked."
You can write a function in an otherwise mutable and imperative environment like Python:
pi = 3.15 # damn, typo for the value of pi! pi = pi * 15 # mutate it, wrongly again
def sum(a, b): return a + b print sum(7, 8)
Please tell me how sum() depends on pi. The program correctly prints 15.
Each function only depends on the arguments passed in, and variables outside of its scope which are referenced. However, the same is true of every function called inside the function, and the dependency is transitive, so the impact of changes to variables that aren't explicitly threaded through leaks outside to functions that never mention them. That's the problem. Hidden state.
It's also the kind of issue that usually looks incredibly contrived in small (say blog-post-sized) code samples, but isn't funny anymore when you have to deal with big lumps of spaghetti code. Global variables make reasoning about dataflow hard. Real problem, poor description.
Imagine you have a 'mutable' program and I have a line that says something like this.
(defun frob (a b) (+ (foo a) (bar b)))
Not only is frob dependent on the definitions of foo and bar, it is also dependent on the the definitions of any mutable globals that are within foo and bar.
If you imagine your program as a directed cyclic graph of functions, and pretend that mutable globals are really functions that set or get a position in memory, you can see that adding more globals to your program (and using them) increases the complexity of your graph mostly by adding cycles to it. (As well horizontal jumps across the entire graph).
It is true that sometimes you need these cycles to write a program (vs. a program that causes your computer to heat up and do nothing), but it is not wildly inaccurate to say that using them all the time makes your program more complex.
I'm not even sure that I'm on board with the supposition that global variables shouldn't be in the language; (saying 'shouldn't' exist at all to a language feature is entirely non-pragmatic).
I think it is more accurate to say that they should be in the language, but they should have a cost greater than locals, and it should be best practice to use them as little as is reasonably possible.
Pretty much the last decade of software engineering has been a slow realization how how untenable the use of large amounts of global state is.
You asked :)
In a language like Haskell the type signatures clearly indicates which global state the function (and other functions called by it) has access to, which makes it a lot easier to reason about side effects, even for code where you haven't read the source.
Of course, this approach also have its downsides. If you decide for debugging purposes to add a logging function to an otherwise pure function deep inside a pure part of your program, you may have to change a whole lot of code.
Yes, anything _can_ depend on any and all globals, but with proper practices, documentation, code reviews and what have you, one can make simplifying assumptions on what _will_ happen.
Or you can leave out the practices, documentation, and code reviews and use a functional language and get the same result. Yes, those things are useful for other reasons, but the current argument is about avoiding the inherent dangers of global variables.
Every time I see someone "explaining" what is going on in a functional language, it looks just like a Max patch! There are multiple inputs attached to multiple outputs. Of course, if you're well familiar with whatever textual based language you're working with, you are visualizing this process, but still, why aren't all functional languages moving in this direction?
The great thing is you can still drop text-based code in to one of these graphical dataflow languages.
I write a LOT of code in Max/MSP and Quartz Composer. Thankfully, both support Javascript, because some things are just plain easier if you can drop back in to a procedural, text-based language.
Even writing your own compiled plugins/externals is pretty straightforward. Quartz uses Obj-C and Max/MSP uses C++, although I think you can mix in or use pure Obj-C as well. I haven't had to write any functionality in to Max that wasn't already available with the core package or from CNMAT, UBC Toolbox, etc. (yet!)
Anyways, what I'm getting at is that there is a huge advantage in laying things out in a graphical way, especially for concurrent programming.
What I find interesting is that there is very little support for these languages. There are no patterns, best-practices, etc... I've had to bring over the basic concepts from other text-based languages when I'm writing a larger project... and often when I'm looking at other people's patches I'm astonished at the lack of maintainability, the amount of code repetition, etc... it can easily turn in to a spidery mess if you're not used to what you're doing!
That being said, Max is still missing some very important aspects, however, such as iteration and recursion.
Interestingly, Quartz Composer does have a method for iteration, one that I frequently use... it works really well, actually!
I really like text-based programming, but I'm increasingly seeing the benefits of graphical programming.
Shared state should be avoided, if possible and pragmatic. Now go do this in whatever language you wish.
Sometimes you see people making fun of programmers who have eliminated the pile of global variables by passing around a pointer to a structure containing all the variables instead. But this actually is an improvement because the type signature of each function now indicates precisely what state can be modified.
The way I see it, when you face a programming problem you mentally divide it into subtasks and use solve each one in the most natural, sweet-spot manner learned from experience. Have to parse a minilanguage? Use an FP approach. Control UI widgets? Do it in OOP style, that's what it was designed for. Keep track of global game state? Just write it imperatively and be done with it.
As a professional you need to understand the good ideas in your field, but as a responsible worker you need the wisdom to avoid pushing those good ideas beyond their proven area of application. One day you'll read an article somewhere that will demonstrate how Haskell is a great fit for ragdoll physics simulations (for example), like it happened before with Parsec for parsing (another example). Until that day... don't rush in blindly believing that purity is better for every class of problem. It might be, but we don't have anything resembling a proof yet. So learn from experience, and subdivide.
An example from a couple of months back. I was using an open source library for parsing LaTeX code before it was added to a search index. After getting the alpha version up and running I ran into a subtle bug - malformed input such as "$$ \dot{C}O_2" would break every future parse of any mathematical input. So somewhere in this 20k loc library a global variable is not being reset after parsing. Since this is an imperative language there is no way to figure out which part of the call chain is calling which global variables so I potentially have to read every line of code of find it.
After two days I managed to track it down by writing a script that reads the source code to identify every static global variable in the code and tracks their values between calls. This is not the ideal way to find bugs.
Which is kind of the point: in an imperative language, you can capture all that complexity -- all 316 arguments, even -- with a few quick hacks to the side effect behavior of your functions. And that's a feature. It makes things simpler sometimes (obviously it can hurt, too -- tools can't substitute for taste). And it just can't be expressed in functional terms.
Incorrect. Say you want to read some global state:
a = do
info <- ask
return (info + 1)
b = do
info <- ask
moreInfo <- a
return (info + moreInfo)
runReader b 42 -- ==> 42 + (42 + 1) ==> 85
This lets you weave information in as deeply as you want. If b doesn't need "info" but a does, and b calls a, that's still fine. You don't need to change b to make the information available to a. Just like a "global variable".You can also accumulate arbitrary information:
fib :: Int -> Writer [String] Int
fib 0 = tell ["fib 0"] >> return 1
fib 1 = tell ["fib 1"] >> return 1
fib x = do
tell ["fib " ++ show x]
a <- fib (x - 1)
b <- fib (x - 2)
return $ a + b
runWriter (fib 4) ==> (5, ["fib 4", "fib 3", ...])
Again, very much like a global variable holding log data, but without many undesirable possibilities (like clobbering the log history accidentally, or performing IO during the computation) that are inherent in "global variables".And of course, you can read and write state, if you want:
inc :: State Int Int
inc = do
old <- get
let new = old + 1
put new
return new
fib 0 = inc >> return 1
fib 1 = inc >> return 1
fib x = do
inc
a <- fib (x - 1)
b <- fib (x - 2)
return $ a + b
runState (fib 25) 0 ==> (121393, 242785)
This is just like imperative programming, except easier and more fine-grained. Note how the number of times "fib" has been called is scoped to the "runState" invocation, and that we don't even need a "variable name". And as with Reader and Writer, if a called function needs the State, the calling function doesn't need to pass it in.So basically, many variations on the hacks that imperative programmers can be implemented easily in functional languages. Imperative programming languages give you a box to do with as you please, but functional programming languages can give you a box that you can prevent yourself from accidentally misusing. The best of both worlds.
The GP's example is not a _global_ variable, by the way. Global variables are not really available in Haskell, except if you use unsafePerformIO:
import System.IO.Unsafe
{-# NOINLINE global #-}
global :: IORef Int
global = unsafePerformIO $ newIORef 0It's nice to know what your software does without having to guess and pray. That is worth saying "runState foo 42" instead of just "foo".
Isn't that the point of this whole argument?
Consider:
foo <- bar
the same as: var foo = bar();
and: foo >>= bar
like: var x = foo();
return bar(x);
and: foo >> bar
as: foo();
return bar();He goes on to say this:
> Would you ever intentionally write a method signature that takes 316 arguments? Would you use any library that contained such a function signature? No? Then why are you using tools that force such craziness upon you?
So we're now expecting to get a conclusion, namely that functional programming is inferior to imperative programming, because you get functions with 316 parameters that thread the state through the program. But no, despite setting up an entire argument against functional programming he acts like he's arguing against imperative programming:
> Of course, there is a place for mutable, imperative programming.
I'm very confused.
I expected a different conclusion from the one you describe, so it didn't sound twisted to me.
In the last question you quote, he refers to "tools". The question will only make sense if you understand that he is referring to imperative tools, not functional ones.
because you get functions with 316 parameters that thread the state through the program.
He assumes that you would never write a single function which depends on 316 different parameters, but that there could be 313 global variables.
Now you have a function which takes three arguments.
His argument is that to understand what this function depends on will require reading all the code in the function and all the code in every function which it calls, to work out which global variables it accesses. Without doing that, you have to assume that this simple function depends on all 313 of them, even though you would never write a function which explicitly took that many arguments.
On the other hand, in a purely functional language, you would still have a function with only three arguments, and simply by reading the function signature, you can understand what it will depend on.
The middle ground is to say, hey, but the function really did depend on some of those global variables, so in the real world the function will have more than three arguments. More than three, but less than 316, lets call it five.
But you find yourself with six other functions which depend on the same two global variables, while nothing else does. So, you go off and make that explicit by having one class with two instance variables and a few methods, one of which takes three arguments. And then you ban global variables. Welcome to OOP.
That is not what he said. He said that a function in an imperative language depends on all global state in the program, not could depend:
> The behaviour of every function in a mutable, imperative environment is dependent upon the state of all of the other (variables|attributes|bindings|whatever) in your program at the time the function is invoked.
This is obviously false. And if you did have a function that was dependent on all 316 global variables, then your equivalent functional program will have functions all over the place threading 316 state parameters through the program! That is what I was trying to say in the post above.
Now lets consider the more reasonable situation that the function does not depend on all global variables:
> The middle ground is to say, hey, but the function really did depend on some of those global variables, so in the real world the function will have more than three arguments. More than three, but less than 316, lets call it five.
So what's the advantage of explicitly writing out the parameters? You could do the same in an imperative program: just write a comment above the function definition that says which globals it uses and which ones it modifies. The advantage of mutable state is that you don't have to do this: you can abstract over stateful entities. Consider logging for example. You could have a global variable called "log" that is a list of strings. Then define a log function:
logmessages = []
def log(msg):
logmessages <- cons(msg,logmessages)
If you want to log something in the middle of your program you can just insert a log("message") call. If you wanted to do the same in a functional program you'd have to thread the logmessages variable through your entire program. You need to change all functions that want to log and all functions that call functions that want to log to take an extra argument and to return an extra value (changing the functions so that they return tuples, and all call sites have to unpack the tuple, etc). Which solution is better? The whole point of imperative programming is that you don't have to write these kludges.It gets even worse very quickly if you don't just consider mutable global variables, but also mutable objects. It is not at all obvious how you'd translate an imperative program that mutates objects into a functional program without essentially writing an interpreter for an imperative language. There are papers on doing this when there is a single reference to the object that gets mutated (Zippers), but the real power of state comes from having multiple references to a single stateful entity.
Imperative programming has native support for stateful entities (or you could say has native support for time), in functional programming you have to manage time yourself. It's like manual memory management vs garbage collection.
Haskell does a pretty good job, by requiring functions that have side effects to show it in their type signature. If you're doing Haskell right, the number of functions that have side effects is very small as a proportion of the number of functions you'll write in total, so most functions you write will be purely referentially transparent. Of course, I'd argue that this is true if you're using any high-level language properly, except possibly in GUI programming.
Unless you're using threads and locks. Then it's probably a safe bet that it will fail in ways you don't expect. ;)
That's why there is a movement in the software community to consolidate threading portions of the code and boil them down to the simplest constructs that could possibly work. It reduces the debugging load significantly. And that's why a lot of time is being invested developing strategies that are atomic and insensitive to threaded programming, because it lets you sidestep the problem entirely. Functional programming is just the leading edge of this trend, it's already sweeping through the imperative and objective world. Things like Software Transactional Memory and Erlang's thread management are examples of tenable approaches. These approaches are a roadmap of how we can move forward and make truly and clearly reliable threaded programs. Programs that don't have land mines waiting for a 1:1000000 shot to set them off and inexplicably ruin everything.
This isn't about "finding your own way." It's about groping for any handhold as we're about to fall into a dark precipice of totally unmanageable massively-parallel and/or distributed code.
P.S. Haskell only seems harder to you because you're clearly not dealing with all the problems that mutable state brings. It's much easier to ignore something than to learn to deal with it.