Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> A "function" is a "function" if it always returns the same result for the same arguments, right?

Yes. This matches the definition of a mathematical function that I gave above.

> how can I call a function which takes no arguments but which returns a value the user entered on the keyboard?

You can't. If your language allows you to write such a function then I would argue you are no longer doing FP (at least in this part of the code).

I believe that this is why many people like languages like Ocaml, F#, Scala. These are hybrid functional-OOP. You can use FP for the parts of the program that it's suitable for, and OOP/mutable-imperative for the other parts("the best of both worlds").

The purist FP programmers might claim that reading input from a keyboard is "uninteresting" and that programming is really about logic, algorithms, and computation, and therefore FP is enough for them (...in their ivory tower).

I personally am a fan of Haskell, but I don't like calling it FP, because it does allow you to write "code" that reads input from the keyboard. They still claim to be pure FP by using a loophole and saying that "getLine" is not a "function" but rather an "IO action". But to me it doesn't matter what you call it, you are still effectively allowed to write code that does I/O and mutates external stuff, doesn't matter if you call it a "function" or something else.

Of course the big advantage that Haskellers claim is that the language enforces the separation of effectful code from "pure" functions. In the other hybrid FP languages it is possible (and happens often) where you have a deep call chain of "pure" functions and then you realize you need some small side-effect somewhere, so you just add it. Haskell does not allow this at the language level, and forces you to refactor your code. In my opinion this does lead to cleaner architecture and more maintainable code in the long term.

But to me Haskell is really interesting for a different reason. I don't like to call it an FP language, because like I said, a lot of the code you write isn't really "functional". A better name would be: "mathematical oriented language". And by math I mean that the code is built using logical systems that have mathematical rules that can be reasoned about.

Going back the I/O (reading input from the keyboard): what the Haskell people have done is realized that you can actually model this behavior of user input as a mathematical model. This unlocks a type of thinking that allows you as the developer to go beyond the thinking of telling the computer: "do this, then do this, then do this, if this happens then do this, ...".

An example: the mouse-position on the screen. There are haskell libraries called FRP that model the mouse position as a "signal function" (a well-defined term they invented) that is a 2D coordinate that changes over time. You can then combine this signal function with others to create behavior such as an image that follows the mouse. You can build complete GUI applications using this approach, and the code looks nothing like imperative programming, and more like an electric circuit of different components connected together.

And that is just one example. The Haskell community has also come up with mathematical models for streams (think unix pipes) that allows you to write streaming code that is much more powerful and cleaner then what you see in other languages.

So my summary: Yes, functional programming is limited, but if we go one level deeper, we could say that the essence of FP is really mathematical thinking. And if we embrace that and run with it then we can open up entirely new possibilities.



Great explanation, thanks

> you realize you need some small side-effect somewhere, so you just add it. Haskell does not allow this at the language level, and forces you to refactor your code. In my opinion this does lead to cleaner architecture and more maintainable code in the long term.

I can see it leads to more maintainable code, but there is a cost associated with that. With non-Haskell I can just make a small change into a single "function" to add the side-effect and be done with it. With the Haskell I need to redesign my whole function-pipeline somehow.

That is a good example. Is there some example code you know of that would show how in fact you take a function pipeline of say 5 Haskell functions and then transform it so that one of the functions "simulates" some side-effect?


A common case is when you need to add logging to a function.

There are 2 ways:

1. You cheat and use unsafePerformIO. This is discouraged and I won't go into the details.

2. You convert your inner function from being a "pure" function to being a monadic function over some abstract MonadLogger m. Then you can still call your function in a "pure" context by using a Writer monad for your MonadLogger, but you can also call it from the IO monad and get "better" logging (with real-time output and timing info).

Note that you still do need to convert your entire call chain from being pure to use "MonadLogger".

But I would argue that even in non-Haskell languages you should also do this (meaning you should thread a "Logger" object through all functions). Why? Let's say you don't, and now you want to do logging from some inner function. How do you configure the logging? To which file do you save it? What verbosity level? You will need to use a global/Singleton Logger object. But what if you want to call your function from two different places in your code and use a different verbosity setting for each? You can't. So I argue you are always just better off doing the refactor in all languages. The fact that Haskell forces this means that developers don't have the choice and are forced to do the right thing.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: