I first touched Haskell >1 year ago, and have studied on and off in the time since, though I haven't touched in a while at present due to being too busy with other things. But one thing I got hung up on - okay, this is probably what every newcomer to Haskell gets hung up on - is the idea that imperative programming is disallowed or made more difficult in Haskell. Now I want to write about a realization I had around the last time I was working with it, because I'm not sure that this is the case.
What is the difference between a function and an action, and how does Haskell treat it differently than other languages? This itself was a question I wasn't sure about after weeks of trying to learn it, and still aren't really sure. But where I'm currently at is that.
In other words, functions are a subset of actions. Every function can be trivially converted to an action. In Haskell, I can take this beautiful functional code:
main = getLine >>= (\a -> getLine >>= (\b -> print $ fibbonaci (read a ::Int) (read b ::Int)))
fibbonaci a b = fibbonaciInner a b []
fibbonaciInner a b list
| length list < 10 = fibbonaciInner (b) (a + b) (list ++ [a + b])
| otherwise = list
And convert it to a typical imperative version:
main = do
a <- getLine
b <- getLine
fibbSeq <- fibbonaci (read a ::Int) (read b ::Int)
print fibbSeq
fibbonaci :: Int -> Int -> IO [Int]
fibbonaci a b = fibbonaciInner a b []
fibbonaciInner :: Int -> Int -> [Int] -> IO [Int]
fibbonaciInner a b list =
if length list < 10 then
fibbonaciInner b (a + b) (list ++ [a + b])
else return list
See what I did there? I made the fibbonaciInner
function an IO action that takes arguments, does a computation, and "returns" its value, instead of a function that merely defines a mapping of values.
(Haskellers more experienced than me often say that Haskell return
has nothing to do with what that word means in other languages, but I think it's actually a very good analogy.)
And what this made me realize is that every other language is doing the latter. In a way, is a pure language strictly better than non-pure ones?
Haskell doesn't stop you from doing things imperatively. Other languages force you to be imperative, so that having the option to be pure feels like a constraint.
All I did by converting the function to an imperative style was lose the ability to use it in pure contexts. I can't import the module and reverse $ fibbonaci x y
now (I can use fmap
to get around it, but with the pure version I don't have that extra layer of complexity). But in imperative languages, everything is already impure, even if it doesn't need to be.
I think if you get past seeing it this way, Haskell starts to seem a lot less arcane.
But then, this is coming from someone who still thinks Haskell is arcane and doesn't understand it, so...
Discussion (0)