“Haskell is a valuable language for a lot of different reasons, but the most important one is that it changes the way that you think about programming. Haskell does things in a very different way from the imperative languages that most of us are familiar with. And it’s an extremely well-designed language, so there isn’t a ton of crap standing between you and the concepts that you can learn from it.”
If you are interested in functional programming and that you want to read a good article that doesn’t use strange terms without explaining them (currying?) or vaguely explain them (Monad?). Which doesn’t contain stuff like “it’s equivalent to ‘((λ x . (λ y . y < x) x)’ in lambda calculus” or which isn’t in a “white paper” format ( http://www.math.chalmers.se/~rjmh/Papers/whyfp.pdf )
read this :
http://www.defmacro.org/ramblings/fp.html
Read it. Seriously, do it.
Even if you don’t plan to program anything in a functional language at all, it’s still damn interesting to read.
Thanks for posting those. I was going to post them if someone asked the old “why functional” question.
The Defmacro article really hits the nail on the head for people that don’t have any prior experience with functional languages.
If you’re wondering “why functional”, read both of those links.
I read it, lost me right off the bat around paragraph four or so …
Maybe if I was already a elite programmer that understood everything and was looking at it as a ‘additional’ language then it may be obvious.
Functional programming is a completely different frame of thinking than traditional programming. It’s something you either get or don’t get. Most people really don’t get it.
For the people who do get it, it seems to be a really great method of programming. However, even among people with Computer Science degrees, very few people grasp it.
Probably the biggest problem with functional programming is that the people who do get it refuse to people that it just doesn’t click for everyone else. Saying “It’s as simple as <lambda expression>” to the average programmer is like saying “It’s as easy as <list of unix commands>” to the average computer user.
As for myself, I can see where it’s useful for doing calculations and math brain teaser type problems, but for your average everyday computer tasks, I can’t even begin to see how it would work.
Functional programming is at its essence quite simple. IMHO, languages like Haskell aren’t great introductions to functional programming, because they bury it under concepts like monads, referential transparency, strictness, pattern matching, elaborate type systems, etc.
At its heart, functional programming requires one thing: lambda. A lambda is just a function that can be passed around as a value, with the usual convenience that can capture variables in its scope if written inline inside another function.
With just that one concept, you can write all sorts of cool functional programs without ever grokking monads. Consider the simple example of finding the maximum value in a vector. What’s the imperative code look like?
mx = vec[0]
for i in vec
mx = max(mx, i)
(Assume you have a max that just takes two arguments)
What’s the functional code look like?
mx = reduce(max, vec)
Just at the very practical level, you get rid of two lines of code and a level of nesting. You’ve now also eliminated all mutable state, but you can benefit from this without ever knowing what “mutable” means!
Please mod the above comment up.
Indeed, the benefit of FP is lambda. Put lambda in a any imperative language, and watch how many things in it become quite simpler.
Haskell is not the only functional language…there is also ML, which is simpler, Erlang, which excels in distributed environments, and of course Lisp/Scheme, which thrives in providing solutions where other languages fail.
One thing that irritates me though with Haskell is referential transparency: it makes things more difficult than it ought to be. I do not believe assignment/destructive update is a problem. The problem is dependencies to values: imperative programming languages like C/C++/Java/Ruby/Smalltalk/Python do not force a style of programming that minimizes dependencies. Assignment allows dependencies to be freely hijacked…for example, an iterator which is modified in the wrong way can cause havoc in an algorithm.
Referential transparency is one of the core benefits of FP. However, I agree that sometimes it just gets in the way with stuff that can not be easily made referentially transparent, such as IO.
World-passing and monads are of course a correct solution. But take a look at the language “clean”. They have something called uniqueness typing which lets you use mutable objects in a functionally pure way.
http://en.wikipedia.org/wiki/Uniqueness_type
Clean also has the advantage of being very fast and very lightweight. It absolutely spanks Java on the debian shootout.
> Clean also has […]
Wake me up when Clean has decent unicode support.
Seriously, I can’t understand how so many programming languages have so moronic text handling. Ruby is a prime example, not least because it’s quite new, of this absurdity with its each-character-is-eight-bits-and-I-won’t-even-tell-which-bits-are-whic h-characters line of thought. I think text handling is needed all the time, so IMNSHO language designers should put a lot of effort in designing a good text handling base for the libraries to use.
Wake me up when Clean has decent unicode support.
That’s irrelevant to me.
My perspective on ‘functional’ languages is that functional can also be a style applied to a language, not an attribute implemented into a language. For instance, it is possible to only use a subset of a language like C or LISP while coding in a functional style. When you code in a functional style you can achieve 90% of the benefits of a functional language when coding without global variables, and minimizing local variable use, and returning computed values instead of storing them. Though this is not so true with VB, some languages can be mostly programmed in a functional style and get the benefits therein.
Some languages make coding in a functional style easier than others. For example, its ALMOST possible to code in a functional style in C++, and indeed certain STL idioms beg for a functional style, but without lambdas, the idea is pretty dead in the water. Not being able to implement the major functional primitives (map, reduce, curry, etc) in a generic and sensical way is a major impediment to doing functional programming comfortably in a language.
Edited 2006-11-27 21:34
Why Haskell?
Agreed, the author’s raised the point but didn’t really answer..
As for ‘modular programming’, there is a paper on Scala where the author says that Scala’s mixin feature reduced by two the number of line needed to make a compiler is Haskell better in this respect?
I don’t know.
As for ‘modular programming’, there is a paper on Scala where the author says that Scala’s mixin feature reduced by two the number of line needed to make a compiler is Haskell better in this respect?
I don’t know.
Note that the most complete implementation of Perl 6 was written in Haskell. Haskell is a very good language for writing compilers and parsers.
I learned some basic Haskell and now every time I find myself programming in a different language I keep finding myself facing a nasty problem thinking, damn it if only I was using Haskell I could solve this in 2-3 lines. Very infuriating! You really should avoid putting yourself in the same situation
That being said whenever I’m stuck with a problem in Haskell I keep thinking to myself, damn it if only I was using Python I could solve this in 2-3 lines.
Wake me up when the libraries of the standard haskell implementations have decent unicode support.
If I could sum up an answer to “Why functional?” in a simple statement, it would be “It lets you reason about your program better”.
Let me give a little example to explain this:
my_map fn [] = []
my_map fn (x:xs) = fn x : my_map fn xs
This is your standard map function in Haskell. Notice that there appears to be two definitions of it. The first one takes the function to apply to the elements of the list and an empty list. You’ll notice the second one has the (x:xs) as the list. All this does is decompose the list into it’s head (x) and it’s tail(xs). If map is handed an empty list, it just return an empty list. If not, it gives you the head and tail of the list and you go about your business applying the function recursively.
What’s cool about this is that I don’t have to put some conditional logic in the same function definition to check for the empty list. Haskell handles that for me.
That’s the beauty of pattern-matching. I can decompose my functions into manageable, piece-wise components. But that is just the tip of the iceberg when it comes to pattern matching.
Or to put it another way, a traditional OO programmer will recognize a big long switch statement as a code smell, begging to be refactored into something more polymoprhic, where the compiler will handle the “switching” for you.
That’s the way I look at pattern-matching, and functional decomposition. It’s letting the compiler free you from hand-coding if-then logic.
Yeah, learn ruby instead…
Yeah, learn ruby instead…
Yes, listen to the blogosphere.
I learnt, amongst other languages, basic Haskell in my first year of studying CS. As much was we students hated it in the beginning, having used a functional language at some point certainly helps you even when you’re writing code in a different language. It’s a certain way of thinking that it teaches you.
C# 3.0 is getting a whole boat load of Functional Programming support via LINQ. There’s a nice tutorial on Functional Programming in C# 3.0 here:
http://blogs.msdn.com/ericwhite/pages/FP-Tutorial.aspx
It might be more accessible to those wanting to learn FP than is Haskell (the often heard knock against Haskell is, “It’s a beautiful language but requires a PHD to understand it” (granted, that’s hyperbole)).
Please have a look in this blog I wrote; it contains counterarguments to the defmacro article:
http://warp9point99.blogspot.com/
I agree to a degree. I am a big fan of FP, but I have no illusions that I could explain monads to your average java or visual basic programmer, ever.
And an imperative language that encourages immutable structures wherever possible while still allowing mutable structures would be a big step forward.
But do you know clean? It is a strict functional language that allows destructive updates by using a concept called uniqueness typing.
It has all the benefits of FP without having to resort to something as complex as monads.
In fact, it is easy to write an in-place quicksort in clean.
The problem with your blog is that the whole thing is based on “we can program in a functional style in any language”. Yes, we can do OO in straight C, but is the language conducive to it.
In fact, the one definition of functional programming that is usually given is the absence of side-effects. Most languages that are considered “functional” do allow side-effects though.
Of course, there’s really no comparison to the type system of something like Haskell to something like Java, so there’s no style of programming advice you can give to overcome that. And then there’s pattern matching and such.
The problem with your blog is that the whole thing is based on “we can program in a functional style in any language”. Yes, we can do OO in straight C, but is the language conducive to it.
Obviously you did not read all of it.
My message is not that “we can do functional programming in any programming language”.
My message is that “imperative programming languages are not as good as they can be” and that “referential transparency is not really necessary”.
Obviously you did not read all of it.
I did.
My message is that “imperative programming languages are not as good as they can be” and that “referential transparency is not really necessary”.
Necessary and desirable are two different things.
Necessary and desirable are two different things.
But referential transparency can also be achieved with a language that allows assignment of local variables as well.
And before you yell ‘impossible’, think about it.
And then there’s “lazy” evaluation, which really changes the rules of the game in a profound way.
There’s no point in learning Haskell if you’re not into it – minimal employment potential etc. – but don’t think it’s a waste of time because you can more conveniently just do the functional thing with Ruby or something. Haskell usually takes some time to soak in, because it’s quite different. I have given up on it a couple times because of various impracticalities, but have been sucked back in because really nothing compares.
Haskell’s authors and gurus tend to be very mathematically inclined, and that’s great for mathematical rigor but sometimes leaves things cloaked in abstruse explanations. They’ll tell you with a straight face that you need to know category theory to understand monads, for example. You don’t. When they start with the math stuff, just tune them out and keep writing Haskell programs. I’m not saying it’s an easy language to understand – there’s plenty of stuff I don’t get, so I don’t really know how hard it is! It just doesn’t take a PhD. (The hard part, if you want to know, is understanding evaluation strategy well enough to avoid stack overflows and similar resource problems.)
I agree with you that people tend to freak out and think you need a PhD in category theory to understand Haskell. Most people when they’re starting out just need to understand the do notation.
Haskell source code is surprisingly clean with it’s indentation syntax (if you want), and lack of commas or parentheses for function arguments.
Here’s a simple little OpenGL tutorial program for Haskell. The indentation will get mangled with the post, but most people should be able to get the gist of it. Looks pretty easy to read to me.
—
— This code was created by Jeff Molofee ’99 (ported to Haskell GHC 2005)
—
module Main where
import Graphics.UI.GLUT
import System.Exit ( exitWith, ExitCode(..) )
import Data.IORef ( IORef, newIORef )
increment :: GLfloat
increment = 1.5
initGL :: IO ()
initGL = do
clearColor $= Color4 0 0 0 0 — Clear the background color to black
clearDepth $= 1 — enables clearing of the depth buffer
depthFunc $= Just Less — type of depth test
shadeModel $= Smooth — enables smooth color shading
matrixMode $= Projection
loadIdentity — reset projection matrix
Size width height <- get windowSize
perspective 45 (fromIntegral width/fromIntegral height) 0.1 100 — calculate the aspect ratio of the window
matrixMode $= Modelview 0
flush — finally, we tell opengl to do it.
resizeScene :: Size -> IO ()
resizeScene (Size w 0) = resizeScene (Size w 1) — prevent divide by zero
resizeScene s@(Size width height) = do
viewport $= (Position 0 0, s)
matrixMode $= Projection
loadIdentity
perspective 45 (fromIntegral width/fromIntegral height) 0.1 100
matrixMode $= Modelview 0
flush
drawScene :: IORef GLfloat -> IORef GLfloat -> IO ()
drawScene rtri rquad = do
clear [ColorBuffer, DepthBuffer] — clear the screen and the depth bufer
loadIdentity — reset view
translate (Vector3 (-1.5) 0 (-6.0::GLfloat)) –Move left 1.5 Units and into the screen 6.0
rt <- get rtri
rotate rt (Vector3 0 1 (0::GLfloat)) — Rotate the triangle on the Y axis
— draw a triangle (in smooth coloring mode)
renderPrimitive Polygon $ — start drawing a polygon
do
color (Color3 1 0 (0::GLfloat)) — set The color to Red
vertex (Vertex3 0 1 (0::GLfloat)) — top
color (Color3 0 1 (0::GLfloat)) — set The color to Green
vertex (Vertex3 1 (-1) (0::GLfloat)) — bottom right
color (Color3 0 0 (1::GLfloat)) — set The color to Blue
vertex (Vertex3 (-1) (-1) (0::GLfloat)) — bottom left
loadIdentity
translate (Vector3 1.5 0 (-6::GLfloat)) — move right three units
rq <- get rquad
rotate rq (Vector3 1 0 (0::GLfloat)) — rotate the quad on the x axis
color (Color3 0.5 0.5 (1::GLfloat)) — set color to a blue shade
renderPrimitive Quads $ — start drawing a polygon (4 sided)
do
vertex (Vertex3 (-1) 1 (0::GLfloat)) — top left
vertex (Vertex3 1 1 (0::GLfloat)) — top right
vertex (Vertex3 1 (-1) (0::GLfloat)) — bottom right
vertex (Vertex3 (-1) (-1) (0::GLfloat)) — bottom left
rtri $= rt + increment –increase the rotation angle for the triangle
rquad $= rq + increment –increase the rotation angle for the quad
— since this is double buffered, swap the buffers to display what was just
— drawn
swapBuffers
flush
keyPressed :: KeyboardMouseCallback
— 27 is ESCAPE
keyPressed (Char ’27’) Down _ _ = exitWith ExitSuccess
keyPressed _ _ _ _ = return ()
main :: IO ()
main = do
— Initialize GLUT state – glut will take any command line arguments
— that pertain to it or X windows — look at its documentation at
— http://reality.sgi.com/mjk/spec3/spec3.html
getArgsAndInitialize
— select type of display mode:
— Double buffer
— RGBA color
— Alpha components supported
— Depth buffer
initialDisplayMode $= [ DoubleBuffered, RGBAMode, WithDepthBuffer,
WithAlphaComponent ]
— get a 640 x 480 window
initialWindowSize $= Size 800 600
— window starts at upper left corner of the screen
initialWindowPosition $= Position 0 0
— open a window
createWindow “Jeff Molofee’s GL Code Tutorial … NeHe ’99”
— register the function to do all our OpenGL drawing
rt <- newIORef 0
rq <- newIORef 0
displayCallback $= (drawScene rt rq)
— go fullscreen. This is as soon as possible.
fullScreen
— even if there are no events, redraw our gl scene
idleCallback $= Just (drawScene rt rq)
— register the funciton called when our window is resized
reshapeCallback $= Just resizeScene
— register the function called when the keyboard is pressed.
keyboardMouseCallback $= Just keyPressed
— initialize our window.
initGL
— start event processing engine
mainLoop
Haskell source code is surprisingly clean with it’s indentation syntax (if you want), and lack of commas or parentheses for function arguments.
That’s a good property of Haskell, but it is no way derived from its functional properties. Basic is also surprisingly clean, given that it is a line based language.
The program you posted could easily be done in an imperative language that looks like Haskell but it does not have referential transparency; the program would be just the same, without the monads.
Here is another challenge for you: an application with an object model that follows the model-view-controller pattern; it should be able to have multiple views attached to the same data, and the views should be automatically updated when the data change.
It’s not difficult to do (so they say). Let me give you some hints: Zipper, Arrows.
That’s a good property of Haskell, but it is no way derived from its functional properties. Basic is also surprisingly clean, given that it is a line based language.
You didn’t understand the context of the post. It was not to demonstrate the functional properties of Haskell.
You didn’t understand the context of the post. It was not to demonstrate the functional properties of Haskell.
But Haskell has advantages over imperative languages because it is a functional programming language…isn’t that all of you FP supporters say?
There is something ironic about driving the OpenGL state machine in a language that is supposed to not have mutable state.
There’s no point in learning Haskell if you’re not into it – minimal employment potential etc. – but don’t think it’s a waste of time because you can more conveniently just do the functional thing with Ruby or something. Haskell usually takes some time to soak in, because it’s quite different. I have given up on it a couple times because of various impracticalities, but have been sucked back in because really nothing compares.
Actually I have picked it up quite fast…I knew ML, so it was quite easy. You see, Haskell is not all that different from other languages…once you know a few, you can easily learn more.
But I see no point in referential transparency. It only hinders my productivity instead of helping it. I think referential transparency is unnecessary, and I have not read a single compelling argument about it, other than it generally helps.
Of course all the gurus will now try to prove me wrong…
But I see no point in referential transparency. It only hinders my productivity instead of helping it. I think referential transparency is unnecessary, and I have not read a single compelling argument about it, other than it generally helps.
You’re not alone. There’s a guy that has been recently been making the same case on comp.lang.functional. No one ever accuses me of being a purist, so I’ll reserve judgement.
You’re not alone. There’s a guy that has been recently been making the same case on comp.lang.functional. No one ever accuses me of being a purist, so I’ll reserve judgement.
Do you mean this?
http://groups.google.com/group/comp.lang.functional/browse_frm/thre…
My view is different from the one in the above thread.
I understand that assignment of any variable from a function breaks referential transparency.
What I am claiming is that if we restrict assignment to local variables, referential transparency is maintained.
If you really only allow assignments in local variables, it is just a shortcut that lets you write imperative programs easier. But to preserve referential transparency, you would have to make sure that the variable can not be assigned to from a closure that can escape the local scope.
And I think limiting destructive updates to local variables would be a bit limiting unless you allow local functions to manipulate the local variable. And if you do the latter, it would be abused to create global variables by just having a function wrapping all the code
I am still not quite sure what your point is.
a) referential transparency can be achieved without complex constructs like monads? Yes, I agree. See e.g. uniqueness typing.
b) imperative languages could be better by adopting language constructs that are typically included in functional languages, but that are not limited to functional languages: I agree completely.
c) referential transparency is useless? I disagree. Concepts like closures and lazy evaluation are much easier to implement and much safer to use if you have referential transparency. I also find it much easier to understand referentially transparent code.
If you really only allow assignments in local variables, it is just a shortcut that lets you write imperative programs easier.
But it is an important shortcut…one that preserves the advantages of pure FP without its disadvantages: functions are only affected by their input. There are no external dependencies. In order to test a function, you only need to deal with its input.
But to preserve referential transparency, you would have to make sure that the variable can not be assigned to from a closure that can escape the local scope.
The type system could see to that by forcing immutable variables inside the closure.
And I think limiting destructive updates to local variables would be a bit limiting unless you allow local functions to manipulate the local variable. And if you do the latter, it would be abused to create global variables by just having a function wrapping all the code
A local function would not be able to manipulate a local variable of the outer scope; the variable would have to be an argument to function in order to be manipulated.
Personally I do not see how it is limiting.
referential transparency can be achieved without complex constructs like monads? Yes, I agree. See e.g. uniqueness typing.
Yes, uniqueness typing is another solution. But I do not see why I have to use special rules in my programming language when certain restrictions are more than enough.
referential transparency is useless? I disagree. Concepts like closures and lazy evaluation are much easier to implement and much safer to use if you have referential transparency. I also find it much easier to understand referentially transparent code.
I do not agree that lazy evaluation is of any usefulness. It certainly can make writing things like the hamming numbers easier, but such tasks do not seem of any usefulness in reality. In other words, I am in the strict evaluation camp.