The Functional Style (2018)
codurance.com
codurance.com
At a high level, many e.g. Clojure programs are indistinguishable from an OOP program. Functional composition and immutability are just superficial details - another 'skin' containing the same stuff.
Does it really matter if you expressed something as a `for` or as a `reduce`? Is local mutability distinguishable from immutability?
Worth noting, real OOP webapps do very little in-process mutability. They're 'functional': request in, response out. State actually lives in Redis/Postgres so the actual mutability story is the same for FP and OOP langs.
That's not to say this functional-ish style is wrong or wasteful. It's a nice step forward. But we really ought to appreciate "strictly segregated side effects", namely actually pure functions, without exceptions.
There's no such enforcement in a great deal of FP langs, and IMO there really should be (at least in the form of a lightweight linter) if we want FP to be genuinely superior.
This is one of the best definitions I've come across.
We can segregate side effects very well in imperative code.
The segregation of side effects is clearer in a strictly evaluated imperative language than in a lazily evaluated functional language.
Functional programming can include side effects.
This is still basically functional:
1> (let ((cntr 0))
(list (mapcar (lambda (x) (inc cntr) (* x x)) '(1 2 3)) cntr))
((1 4 9) 3)
Just not "purely functional". Functional means we work mostly by applying arguments to functions, and avoiding loops, ifs and gotos in favor of canned mapping operations that work with sequences and such. If an assignment sneaks in here and there, it's still functional. (if antecedent consequent alternative)
is not functional because control flow is being diverted by a special operator without the use of functions. But if we have a function iff: (iff f-antecedent f-consequent f-alternative)
such that iff will call the f-antecedent function and then either call f-consequent or f-alternative based on what f-antecedent returns, then that is functional.Functional just means "obsessed with functions"; without further qualification, it has nothing to do with purity. That's just certain politico-religious camps trying to hijack the word for their own agenda.
[0] It doesn't really matter what type of purity we are talking about here. Whether it's about pure functions as in FP or Rust code that never depends on unsafe code.
With that in mind, you really can’t do FP without automatic currying and the function composition and application operators (. And $) in Haskell. Without these, you lack the key components needed to work with your only means of abstraction. Language author seem to think lambdas are enough, but if you can’t compose functions to build new functions, partially apply them to get specific versions of behavior, then you aren’t going to get very far.
they are of a person doing the hacker rank questions, but in haskell. The last video (about the magic squares) is probably the most informative for your query.
program = (phase 3 . (phase 2 . phase 1))
The input of program is fed to the first phase, then the second phase, then the third phase which will produce the output of the program. Each phase can be replaced or the pipeline extended as long as the inputs and outputs match. This is an infinite composable and is the basis of how FPers architect applications, by splitting each phase in their own pipelines, smaller and smaller until you get to very basic functions. We like it compared to OO because it's straightforward to understand the flow of data. There is no need for a complicated object graph and any piece can be replaced if the types are correct.
As in OO with inheritance (maybe not the best comparison), you also need a way to specialize functions in order to get their signature to match the input and output requirements of a pipeline. For that, we can partially apply a function.
For example, if I need to connect the function:
Int String -> Int (That is a function with two parameters, an int and a string that produces an int) to the output of the function * -> String (a function that takes some arbitrary value and produces a string), I need to be able to partially apply the Int, and get a function that is just String -> Int. In Haskell, all functions are single input, single output. Multiple inputs is just a shorthand for a pipeline of functions. The function
Int String -> Int is really just the pipeline Int -> String -> Int. (You can see how this all starts to fit together).
The function application operator is the most basic, it's just a way of forcing the order of evaluation. When you're building pipelines, you want to be able to construct a pipeline before you apply an input to the pipeline. To get the correct order of precedence between . and application, you use the explicit $ operator.
What I'm trying to convey here is that these operators are akin to inheritance, composition, overloading, etc. in OO. They are ways of working with the basic building blocks of the paradigm to make larger and larger programs. As you can also see, being able to create pipelines is the basic piece of the puzzle, and building pipelines requires automatic currying to facilitate making the pieces fit, while the application operator allows us to control the order of evaluation.
You can't deny that there are lots of languages and techniques that people call "functional" where the key ingredient isn't "programs composed as an evaluation of a single expression" but something else (such as "immutable state").
We FPers borrow or share concepts with OOers. The State monad (our basic building block of holding state) is very much like OO method chaining for example. But those similarities do not make either equatable with the other.
splitname = name |> String.split
splitname.(" ")
Also, it's a stretch to call & currying. It's shorthand syntax for making an anonymous function. If & is currying, then the following JS code is also currying: const cube = a => Math.pow(a, 3)
Elixir doesn't have currying or partial function application any more than JS or C# do. If your definition of "functional" requires these features then Elixir isn't functional (and that's fine).Nowadays, the definition's been deeply watered down, and there are vanishingly few languages that both call themselves object oriented and stick to the original idea. So maybe it's not worth fighting over the terminology anymore. But still, I can't help but feel like we've lost something in the process.
FP didn't become the darling until more recently, and I think that it's just possible that people could hold the line and prevent the term from being watered down. And the original idea was very much one of programs that were composed of smaller parts, such that the whole thing was ultimately just the evaluation of a single expression.
Note that this definition predates Haskell by a decade or so. It was trying to capture what the designers of APL were going for.
But lambdas are enough to define these operators e.g.
(defn comp [f g] (fn [x] (f (g x)))
(defn ap [f x] (f x))
Operators and currying by default can use useful but it's a syntactic issue rather than a fundamental one.Some languages have some of the feature I mentioned, JavaScript has bind for example, but it’s cumbersome and using it as a means of partial application for FP would be an abuse.
These operators need to be syntactically easy to use, ergonomic, and recommended. Notice that in Haskell these are infix for a reason.
Also, in typed languages with generics building those operators is more tricky.
I agree syntax is important, but your definition rules out languages like Clojure which most people would consider functional. Haskell programmers might disagree, but I would assume on the grounds of purity rather than the syntactic overhead of partial application.
If your types are well labelled, can you push the complexity of your control flow and functional-glue (as I like to call it) into the compiler?
It is a question of choice.
If every pure function we write prevents another headache, why not seek out opportunities to write in that style, regardless of language?
http://worrydream.com/refs/Backus-CanProgrammingBeLiberated....
Declarativity, compositionality, and referential transparency are all part of how he defines it. But he also takes a moment to contrast his idea of functional programming with a "lambda-calculus based system" (read: lisp), and one of the things he specifically criticizes is that, in those systems, functions aren't automatically curried. (His actual wording was that they are allowed to take multiple arguments, but the meaning is roughly the same.) The gist of the argument is that requiring functions to only be unary limits the number of operations you need to compose functions, which ultimately keeps the system simpler and easier to understand. He compares limiting function arity in functional programming to limiting the number of available control structures in structured programming: Strictly speaking, you lose some expressivity, but it's justified by all the other things you get in return.
Functional programming emphasizes recursion, deferral of control decisions into functions (e.g. pass functional arguments to some function, and let it decide which are called and which are not), and the re-ification of all control mechanisms as functions (e.g. continuations).
If it has loops, ifs and gotos, it is not functional. If it just has function calls, then it's functional, even if there is mutation going on of variables or array elements and such.
Purity plays well into functional programming, because if we have structured the control flows in the program based on function calls, we can determine which functions are pure and which are not, and get a good grip on reasoning about and managing the side effects to avoid surprises.
I find automatic currying by default quite weird.. If you don't provide the correct number of arguments to your function the errors generated won't be nice..
Would a 'curry' operator to provide currying be good enough?
We know that since Wittgenstein at least [1]. Those who forget it, are doomed to lament about "no true Scotsman" distinctions nobody cares about.
A good example is the immer.js library, which takes the pain out of immutability while retaining the most important benefits. Instead of bending over backwards to transform hierarchical data with lenses and the like, you just mutate a ‘draft’ copy imperatively within a bounded context. While it has its limits, it’s often much quicker to write and easier to understand than the equivalent ‘purely immutable’ approach without any real downside.
But I'm not a zealot. It's fun in the right domain.
When those things come from a stream of business problems, Haskell, I hate to say, is a poor tool, for just the reason you stated. You’ll always come across situations where mutating state would make an incremental change very easy (despite being “bad practice” for long-term complexity) but where having rigorously designed a functional system, that mutation or change is now painfully hard and requires significant refactoring.
Because business pressures utterly do not care about engineering properties of the backend system, except for high level summaries of basic properties like overall cost or reliability (metrics that functional programming vs other paradigms doesn’t have much impact on), you end up never being able to justify the upkeep and refactor-as-you-go workloads necessary for the functional implementation to not get bastardized into the exact same sort of spaghetti code mess you get with other languages.
In my experience, the types of errors, bugs and extensibility headaches in a business setting are not addressable with static typing, designs leveraging a type system, formal verification, or immutable design patterns. Those things are fine, not knocking them. It’s just that they cannot help you for dealing with reactive business problems. They don’t live up to the hype about it.
The problems are all about a sudden context change in which a previously valid set of behaviors, performance characteristics, whatever, is instantly rendered invalid because circumstances have suddenly changed, and you need to simply react to it and pray you’re successful enough that the system even lives to some magical future time when you can refactor.
That's not to say that statically typed FP can't be written in such a way to allow this kind of arbitrary modification, but then you get into the realm of finding the perfect abstraction in the type system, which is exactly the same kind of maintenance problem that OO architecture astronauts bring to a code base.
This is all to say -- we should be writing Clojure, which allows a tight focus on actual business requirements while still being flexible enough to allow you to bail out of FP patterns when necessary. :)
In the Haskell systems I write, this change would be trivial. It’s a single line change. Maybe it’s just a benefit of the particular pattern I’m using (standard Yesod handler, which if I remember correctly is just the ReaderT IO pattern), but still, it seems completely wild in my view to trash an entire language and indeed an entire paradigm because you can’t imagine making the change in your example.
Regardless, the point is not that it's impossible to make such a change in a Haskell code base, just that it may or may not require non-trivial breaking changes, while such a change in an OO enterprise code base will always be trivial.
There's lots and lots wrong with OO patterns, but being able to respond to changing and arbitrary whacky business requirements is a clear strength.
Why? From where did you invent this completely arbitrary constraint?
> You shouldn't be putting business logic in your HTTP handlers.
Why not? How else could this work? The user has to interface with the business logic somehow.
There’s nothing at all wrong with a HTTP handler that takes in a request, delegates to a few different business logic things, and then sends a response.
Not only are you moving the goalposts with your argument, you’re also coming up with things that are completely false.
There’s nothing about OOP that inherently makes extending some code to send an email easy. Likewise, there’s nothing about FP that inherently makes the same task hard.
It just happens to be the cheapest and most reliable tech I've come across to run my businesses on.
If you are writing pure functions and total functions, it's functional code. The rest is syntax.
And pure functions are probably best tagged with an asterisk because most of the time you have state to manage somewhere, you're just going to jump through some syntactic hoop (monad) to mask it
> And pure functions are probably best tagged with an asterisk because most of the time you have state to manage somewhere
purity does not mean there is no state! This is exactly what the monad (and similiar structures) in haskell is for, which you correctly mentioned. But they don't violeate the purity.
And this matters in practice, because sometimes the desired effects of your program are inherently stateful. For example, if you want to have lines of text output to the terminal in a certain order, you have to at least think about state at some level. If Haskell was truly free of any state, this would be impossible to guarantee.
What I will agree with though is that it's mostly possible to ignore state in Haskell, and that individual pure functions are truly pure. My point is that those pure functions can't do anything without invoking state at some point.
I mostly agree, but that's my argument :)
> If Haskell was truly free of any state, this would be impossible to guarantee.
I was never claming that haskell was free of state, i was claiming the opposite! That state and purity are not mutually exclusive, they just result in constructions like monad.
There's a great presentation discussing Pedestal HTTP library for Clojure, and one of the slides notes that Pedestal has around 18,000 lines of code, and 96% of it is pure functions. All the IO and side effects are encapsulated in the remaining 4% of the code: https://www.youtube.com/watch?v=0if71HOyVjY
This is a completely typical situation when you're working with a functional language.
Vast majority of the code is written pure functions that can be reasoned about independently of the rest of the application. This is not practical in languages that rely on mutable data because things get passed around by reference. As soon as you pass a reference to an object to a function that's used elsewhere you end up with implicit coupling that's difficult to reason about.
Of course, you could use immutable data structure libraries in imperative languages, but then it's completely on you to ensure that you never put any mutable data, such as an object reference, in those. At that point you might as well use a functional language.
1. high-order functions ( as inputs or outputs ) e.g. map, reduce, filter, etc. Direct benefit is a significant reduction of code and the ability to chain operations together and compose them.
2. immutability. Direct benefit here would obviously be minimizing shared state thus reducing bugs
3. side effects: minimizing and pushing side-effects to "edges" of an application. and/or designing programs such that side effects can be mocked out. e.g. building up a Http request that can later on be executed, thus facilitating unit-testing.
4. type checking: Converting runtime errors into compile time errors ( to some degree ). again, by offloading work to the compiler, you can catch errors much earlier on.
The biggest problem w/ FP ( as I have evidenced ) is that while these things are great, when taken too far for the sake of pure FP itself, can create complex code. so there has to be a balancing act involved.
But you did say "more of the time" rather than "always", so I'm not actually disagreeing with you.
> I find that FP forces too many contortions
In light of their underlying computational models, functional programming really isn't the paradigm that is rife with contortion here.
FP Astronaut -- Super focused on hypothetical, academic, mathematical concerns and random performance optimizations (e.g. tail recursion) over clarity. I think scala's Slick library is an example of this.
FP Pragmatist -- Concerned about clarity, debugging, logging, correctness, readability by all skill levels, modifiability, documentation, the common cases first and the extreme cases last
I think you can be a big FP advocate without being an off-putting incomprehensible blowhard
Iteration is usually more performant than recursion, but doesn't mix well with immutability.
I was looking for that comment to upvote it but it didn’t exist. Everyone is expaining their pet feature of FP.
Maybe we should put aside our differences and lean towards cheering the people who put lots of effort into opening this world up for as many people as possible. Those who are not FP experts need exactly this kind of explanation, not mathematical mumbo jumbo.
I’ll save the URL for sharing with anyone interested.
So here it is again: Great work, thanks for sharing!
let pi = 3.141592
let label = UILabel()
let names = [“Java”, “Perl”, “Swift”]
These won’t accidentally be changed and they’ll never be null.The editor/tools can now make additional checks before I try to run the code.
A small step towards correctness and readability.
It's really nice to structure your program around types and build your program via function composition - but getting it performant (let's say similar to C, Rust or Java) is far from simple. In particular performant Haskell does not look like idiomatic Haskell, whereas performant C, Rust or Java does.
Now don't get me wrong - I absolutely love the idea of functional programming and λ-calculus is a beautiful system to express computation, but in practice it's still hard to make performant while keeping a readable style. Maybe Idris 2 can close the gap?
> the output value of a function depends only on the arguments that are passed to the function, so calling a function f twice with the same value for an argument x produces the same result f(x) each time.
OK, that's basically a static method in C#. Is that wrong?
The piece goes on to say
> Functional programming, therefore, is programming so as to avoid these side effects wherever possible.
So functional programming should never handle state? Do you just move that logic into another framework (Angular, React, etc)?
Maybe there's some obvious stuff I am missing. Why should I use a functional language (like F#) over an existing OOP language?
It it can be true of a static method, but a static method can also make an HTTP request, read the current time, etc, so that the answer doesn't just depend on the arguments.
No, `Console.WriteLine` definitely has side-effects
> functional programming should never handle state
No, quoting the same quote as you did: "avoid these side effects wherever possible", emphasis on "wherever possible". Of course, a program which never has any side effects or state would be useless, as it would not accomplish anything.
https://en.wikipedia.org/wiki/List_of_programming_languages_...
If a language is pure then there is no point running an instruction other than to return a result since the only point in ignoring a result would be if the instruction had a side efffect, which a purely functional language disallows.
Haskell does simulate this chaining instruction via the do notation..
do
a <- getLine
b <- getLine
putStrLn ( a ++ b )
It looks like it is chaining instructions, but really this gets rewritten by the compiler to something like this (in pseudo js) : bind(getLine, function(a) {
bind(getLine, function (b) {
putStrLn a + b;
})
})A more functional style, say like that of the one in F#, strongly encourages you to avoid many occasions where side effects may otherwise appear. Look at this fiddle for instance:
A static method can have side effects and/or return different results even when given the same arguments. e.g. returning DateTime.Now would never yield the same results.
"So functional programming should never handle state?"
Any useful application will have state and side effects, the idea is to push as much of it as possible to the outer edges of your application and keep the core as pure as possible/practical. You can do that with any language, but a functional languages will be designed to make that easy and idiomatic.
Example: It is well known that "Java Strings are immutable", which is a good idea. Yet you can absolutely write: > String str = "hello,"; > str += "world!"; So it's "immutable" for some lawyerish definition which does me no good. If I write a method that depends on s, it will produce different results each time.
> So functional programming should never handle state Not at all. You should know (and be able to enforce) when your code does mutations.
`str` is a mutable reference to an immutable object. You can make the reference point to something else, but the object being pointed to is immutable, so eg if you pass it to a function, that function cannot change it under you. If you want to make the reference immutable, declare it as final. I don't think understanding the difference between a pointer and the value it points to is lawyerish.
Strings being immutable means that you can can pass around references to them, safe in the knowledge that they can't be modified.
For example:
void example() {
String s = "hello";
someOtherMethod(s);
// `s` is guaranteed to still be "hello" here.
}
Or, a live example using repl.it: https://repl.it/repls/BothTrivialQuadrantIt abstracts http handling into a series of key/value data structures. The request that's pulled in looks like this:
{:remote-addr "localhost"
:headers {"host" "localhost"
"content-type" "application/json"
"accept" "application/json"}
:server-port 80
:content-type "application/json"
:uri "/"
:server-name "localhost"
:query-string nil
:body ""
:scheme :http
:request-method :get}
The response like this: {:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"}
Functions manipulate these structures, but their input and outputs are primarily those data structures. (defn handler [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello World"})
Your app is now just a function stack that get's called in this order: (def app
(-> handler
(wrap-content-type "text/html")))
Request => wrap-content-type => handler (generate response) => wrap-content-type => Returned
Want to add middleware? Define a function that handles it, here's an example[1]: (defn middleware [handler]
(fn [request]
;; Do something to the request before sending it down the chain.
(let [response (handler request)]
;; Do something to the response that's coming back up the chain.
response)))
Then add the function to your app: (def app
(-> handler
(wrap-content-type "text/html")
(wrap-keyword-params)
(wrap-params)))
You want to handle session data? Ring provides wrap session[2], add it and then use your own middleware or handler to manipulate the session by just changing the value at :session.Example [3]:
(defn handler [{session :session}]
(let [count (:count session 0)
session (assoc session :count (inc count))]
(-> (response (str "You accessed this page " count " times."))
(assoc :session session))))
Much of your world is now reduced down to:* Does this data structure look correct?
* How can I fix it?
* Do I need some resource like a db connection? Ok how do I add it to my data structure?
It all composes, and you end up worrying a lot less.
Finally I find that doing things this way ends up creating programs that just have much less explicit state, as opposed to temporary state that's derived from other values and so only changes when those things change.
You can program in other languages like this, it's just a bit harder, and you have to hope that no one else you're working with breaks one of the assumptions that a system like this is built on, as opposed to having the language enforce those for you.
Sorry if I've been unclear =)...
[0] - https://github.com/ring-clojure/ring/wiki (Examples taken from ring). [1] - https://stackoverflow.com/a/19459508 [2] - https://github.com/ring-clojure/ring/blob/95e4ca25d5b98c45f9... [3] - https://github.com/ring-clojure/ring/wiki/Sessions
I've never had huge problems with writing programs php or JS. Now everyone is freaking out about typescript and PHP type hints and all I'm doing is sitting here scratching my head asking but why? What does it do for you?
It looks like people were doing a lot of dirty state mutation and a lot of optional parameters that sometimes had mixed types because "then you can use it with an ID, an email, or a user object you silly billy".
Both of those things create a god awful hell. The second example may sound easy to use but then you have now way to reason about code, you can no longer clearly compose pieces and variable names become less descriptive. Types provide you with a simple pair of hand cuffs that prevent this issue. But they come at a cost.
My issue is that people with shitty habits should probably just change their shitty habbits. Changing from a dynamic language that lets you take a lot of safe shortcuts to a static language because you abused those shortcuts is really the wrong way to go.
Moving to something like clojure looks like a much cleaner solution to the problem.
Still handcuffs, but at least youre forcing the right thing (pushing the state mutation to the edge, leaving the business logic clear and rational without all the type safety bloat.
I'm currently maintaining a massive spaghetti code TypeScript nightmare that escape-hatches with @ts-ignore and `any` about every other line. I'm not sure why they even chose to give up all of the imperative, dynamic power of JS and then not even adhere to the pre-processor's own rules.
I think there's an argument for imperative and functional approaches, but I also think trying to change an imperative language into a functional one is a bad idea, and trying to shoe-horn in type safety into a language that doesn't have it is just foolish.
I feel like languages come with trade-offs inherent in their design (Go! trades DRY for type safety, JS trades data validation/type safety for convenience), and if you don't like the trade-offs of the language for your project, then you are probably using the wrong tool for the job. However, when it comes to JS, you usually don't have a choice (unless you go with Dart or WebASM, which I admittedly know very little about).
I suppose TS could have a place (I particularly understand libraries that attempt to improve their data contract using it), but then I wish it didn't provide such wide variety of ways to escape or ignore it's restrictions. I feel like they are fake guard rails and provide a false sense of security.
I actually enjoy them a lot for other reasons, being able to guarantee type is a nice feeling. But it doesn't guarantee correctness. And I think that is the problem.
Developers see lack of type safety as a lack of correctness. Hell, I don't even blame them, a lot of code I deal with is impossible to reason about because of so many bad habbits code review is supposed to catch.
My issue with TS is that people are using it to solve a problem that it does this actually solve: the developers reluctance to code clean.
Yes, that is wrong. Static methods don't have access to object state, but they can still access mutable state (e.g., static data members) and invoke other side effects, so its results can depend on things other than the arguments.
> > Functional programming, therefore, is programming so as to avoid these side effects wherever possible.
> So functional programming should never handle state?
“Wherever possible” is not the same as “everywhere”.
> Do you just move that logic into another framework (Angular, React, etc)?
No, though pure languages generally do something loosely similar; Haskell programs, can be viewed (in one interpretation of values in the IO monad) as having a main function that produces an imperative program as the output, which by convention the runtime executes.