Hacker News new | past | comments | ask | show | jobs | submit login
Awesome Functional Programming (github.com/xgrommx)
69 points by pretext 3 months ago | hide | past | favorite | 59 comments



Stupid question: how do folks generally use awesome lists?

I think it's neat they exist, but they don't really live up to the "curated" promise in my mind. Mostly they seem to be a grab bag of everything under a topic, and it's left to the reader to sort through. As such they've never come to mind when I'm actually researching anything.


The better ones do a better job of organizing and seem (to me) to be more focused. They also offer more explanatory text around a topic/category or about an item.

This particular one covers a very broad topic, and mixes material on both functional programming languages and functional programming style in languages that permit it (at least halfway permit it). Just look at the Books section. It's not alphabetized or subcategorized by language, topic, or difficulty, just a list. There are no descriptions of the texts, and no apparent running theme. A learner should not start at the top of that list and work through it, it would be counterproductive to most readers. It would benefit from being broken down into more sections, like:

  Functional Programming
  - What it is and more universally applicable tutorials/texts

  Languages
  - Haskell
  -- Books (ordered by difficulty and topic)
  -- Tutorials (ordered by difficulty and topic)
  -- Libraries (arranged by category)
  - Elm
  -- (like above)
And accept that some entries will be included multiple times. Like a tutorial that covers both concurrency and networking in Haskell would go in both topics under that language.

Look at this part of a CL one: https://github.com/CodyReichert/awesome-cl#offline. The Beginner/Intermediate/Advanced breakdown is useful, and each title is given a short description. You could, in theory, follow this reading list in order and gain something on each new text as you work down the list, and they'd all be relevant to learning the topic of Common Lisp. It provides a more coherent set of information and resources (at least for that section).


Perfect! I just so happen to be learning common lisp right now. Will give that a try and see if I get more mileage out of that example.


They start out nice when they're first created, but it's really emotionally difficult to run a good one, because to be a good one, you need to say a lot of no, you're not awesome enough to be on this list. Like, far more often than you say yes. And who wants to be that person? A small percentage of people, and that set will be full of people who will make some other big social mistake and blow things up too.

So you end up with lists that really amount to "who asked to be on the list".


I like them when I am new to a technology or programming language. For example last year I spent a significant amount of time writing Go code and doing Solidity/blockchain development.

Being able to scan https://github.com/avelino/awesome-go, https://github.com/bkrem/awesome-solidity, and https://github.com/gianni-dalerta/awesome-nft for general resources, projects, guides, or just general information was a nice resource to have in my back pocket.

I think your point is valid, but it's also a personal expectation of what you get out of the resource. I think the fact that they are open ended helps both developers who are trying to reach an audience and people who are browsing for new tools, ideas, etc.


Ah, I hadn't thought about it from the publishing side. That makes sense. Easier to break into as an unknown writer than google rankings.


> Stupid question: how do folks generally use awesome lists?

I usually put a bookmark and then forget about them entirely. Gives me a little bit of dopamine for a short while though, so it isn't completely in vain.


I've found this exact same problem. It's a huge list of links, I'll open a few projects that might be interesting, save them for later — and then when I look at them, I don't remember the context (why I saved them, what for) and I'll just remove them from my list.

They have their use as a jumping-off-point, but what I _really_ want is a truly curated list. LessWrong has a list of "best textbooks" where you have to provide a "best" and one (or two?) other textbooks that you've read which you didn't like as much, and an explanation on why the former is better. I think it's a great way to curate a list. I wish there were more like that.


I just (for example) took a look at the PureScript part:

https://github.com/xgrommx/awesome-functional-programming#pu...

No link to the 'Purescript by Example' book

https://book.purescript.org/index.html

No mention of Halogen

https://purescript-halogen.github.io/purescript-halogen/inde...

Some links 404

http://blog.sigmapoint.pl/purescript-will-make-you-purr-like...

And it's like that for almost all of these awesome lists, most of which have been abandoned some time ago.


"Purescript by Example" is in the list, but up in Books. And reflects on what I noted in my comment, the information seems to be present in this list but it's not well organized.


Ah, I see, thank you. Although that is a link to the older version, not the recent, forked one. Which proves a point that I had looking at that list, it seems to be from about 5 years ago.

   This repository contains a community fork of PureScript by Example by Phil 
   Freeman, also known as "the PureScript book". This version differs from the 
   original in that it has been updated so that the code and exercises work with 
   up-to-date versions of the compiler, libraries, and tools.
https://book.purescript.org/index.html


I don't because I've usually found they fail hard at actually being curated. Generally if someone decides to make a curated source on a topic they'll actually just make an entirely new source with links to other sources as appropriate - adding the context of when specific links are helpful is valuable and seems to go against the general idea of awesome lists. I'd prefer to read a really good e-book or blog post that just includes their sources or links for further reading.

I don't think a blog post that identifies a problem someone had to solve and the resources and steps they went through to solve it (even if they don't add any real innovation about tool usage beyond their sources) is bad - but even in that format you're getting a lot more context around why some sources are awesome.


I've found that awesome lists are generally good for the dumbest, but still one of the most effective, kinds of learning: pattern-matching.

This particular list isn't particularly organized, but I can still C-f for "lens" and find a few articles. I'll open all of them, read all of them, and by the end have a good shot at understanding what a lens is and how to use one.

Certainly, reading multiple sources gives you a better chance at understanding than any one particular source.

The human brain is extremely good at pattern-matching, and so awesome lists are a great way to "train" it with "data".


I start from the assumption that it probably has everything under the sun for that specific technology and I grep for keywords, hoping that a relevant project that matches my needs jumps at me


I read them when they get posted to HN or Reddit and sometimes leads me to learning new things


Indeed they are probably most useful to the person writing them.


For some scenarios, functional programming is a good match. For example, Big Data. FP has features such as lazy evaluation, which is important for Spark, for example. Not surprisingly many Big Data libs, including Spark, are written in FP languages (Spark was written in Scala).

For JavaScript FP is not a good match [1]. Certainly, many ideas that originated in FP work well in JavaScript, for example lambdas. But some people take FP to an extreme, to the point of saying that OOP is now obsolete. How can OOP only now become obsolete, given that FP has existed long before OOP? That doesn't make sense.

[1] https://medium.com/weekly-webtips/dysfunctional-programming-...


You seem to confuse a few things. Lazy evaluation is an option in the design space of programming languages, and one that is rather exotic due to its complications. Eagerly evaluated functional languages are quite normal. An imperative language could use lazy evaluation as well (and you can easily emulate it in one), but programmers would likely be confused by the ordering of intentional side effects and some typical imperative paradigms might even not work in a lazy setting.

Object-oriented programming has long been criticized, but not in the context of JavaScript. As JavaScript is untyped, the downsides of OO programming aren't that visible. The big divide between (typical, statically typed) functional languages (say ML) and (typical, statically typed) object-oriented languages (say Java) lies in the organization of data. In the former you will typically use algebraic datatypes, which normally force you to define a complete data structure in one place and allow you to define many operations on it everywhere in your code (operational extensibility). The latter do the opposite and allow you to add new subclasses (structural extensibility). See "expression problem" for more info. In practice, it turns out that operational extensibility is much more important for safe code and there are functional patterns that give you the best of both worlds ("finally tagless").

It is because of this design-space divide that some people claim that (statically typed) object-oriented programming is a dead end.


> Lazy evaluation is an option in the design space of programming languages, and one that is rather exotic due to its complications.

It definitely requires control of the data - i.e. having a partially evaluated linked list somewhere directly accessible by pointer without signaling any generating function will break a lazy implementation. That said I absolutely love lazy evaluation and have built a lot of tools in less functional languages like PHP and C++ for handling very large data sets.

When dealing with large report generation (i.e. all transactions from a quarter compiled for the accounting department) it can be necessary to avoid ever actually realizing full data sets into memory for efficiency and capacity reasons - that's an area where abstractly defining various transformations on the result set and then executing them in an iterative or batched manner can be exceedingly useful. I have killed many OOMs in my days.


Just want to add some defense for OOP (and that the two is not orthogonal at all, you can do OOP with FP): not everything is data-first. There are cases where object identity is essential for example, as well as types where the internal state has to be truly encapsulated to uphold invariants. Here, most FP languages would use the convention to only use functions designed explicitly for that very finicky use-case, while OOP can enforce it.


For the latter case you would use an abstract type hidden in some module in ML. Only difference to OOP is the lack of open recursion and implicit "this" argument.


> Eagerly evaluated functional languages are quite normal.

Probably not very efficient. Are there commercially successful examples of such a language?

> An imperative language could use lazy evaluation as well

Certainly! I believe Microsoft's Linq is one such example.


> Probably not very efficient. Are there commercially successful examples of such a language?

Global lazy evaluation by default is the exception, not the norm. Standard ML, F#, OCaml, Scala, ... I actually would have more trouble finding a classic "functional" language that is lazy like Haskell but isn't Haskell.

Not to mention, almost all the multiparadigm languages that can utilize FP like C++, Rust, etc are eager.


Rust does lazy eval.


Basically every language has some kind of opt-in lazy evaluation, Rust is strict by default as is basically every remotely mainstream language bar Haskell.


Expressions in Rust are eagerly evaluated, but you can write lazily evaluated things without issue.


Honestly I've derived enormous enjoyment programming in Clojurescript, I don't think FP was a detriment at all. The way I see it, there's a pretty wide scope where FP or OOP will work just about as well and it's just a matter of preference. For me, FP makes sense in a way OOP never has. I gather that for lots of people, the opposite is true. It's certainly not true that OOP is obsolete though. There a lots of people that prefer it, and they do perfectly good work!


I wish people would stop being such zealots when talking about functional programming. JS is mostly an imperative language, with OO and FP features, that you can use when they make sense. From the article you linked:

> This is a function but it is not functional because it mutates state and uses iteration.

The state and iteration was only internal, JS doesn't support TCO and the algorithm is very simple. Sure it's only an example but it's not a great one. Then it continues:

> To be functional it is not sufficient to simply remove classes and write top-level functions. You have to completely change your way of thinking. The functional way has no side effects or state mutations. It has no assignments, no iteration, and no conditional statements.

That's not "being functional", that's Haskell. I can write a program with side effects, state mutations, assignements, iterations and conditional statements in OCaml or Scala without issues.

> Assignment and iteration are typical of imperative programming. In functional programming changing the value of a variable using an assignment statement is not allowed.

It is allowed though:

    let x = ref 0;
    x := 5 (* x is now 5! *)
Again, that vision of "functional programming" is "strictly Haskell".

> This is because JavaScript lacks a critical feature known as lazy evaluation. Lazy evaluation means not calculating things until they are actually needed.

JS has lazy evaluation with iterators. What it doesn't have is chaining/transformation of those iterators: map, filter, reduce are not defined for iterators. You can make your own prime iterator (with internal state!) and iterate on it though.

> Because of a lack of features such as tail call optimization and lazy evaluation you can’t do “real” functional programming in JavaScript.

No true scottsman fallacy. By the way, Safari implements TCO, so you should be able to do real functional programming on Safari with iterators!

> Functional programming and side effects

Again, that's only for Haskell. And you can write scary imperative code in Haskell too:

    toto x = if x == 0 then error "Ooops" else x + 3
This will add 3 to any integer, except 0, in which case it will raise an exception. This is nowhere in the type of toto. Also, all that mention of "pure and impure" programming and no mentions of monads and promises, which are almost just like monads?

> Among some JavaScript programmers, it has become fashionable to completely avoid classes and OOP because they think functional is better. But given that you can’t do real functional programming in JavaScript, completely avoiding OOP will only eliminate the advantages of OOP while not gaining the benefits of pure functional style.

False dichotomy, you can write plain imperative code in JS, and it works well.

> That’s not to say there are no benefits at all to incorporating some functional style into JavaScript code, but wholesale replacement of OOP with functional style is not warranted.

Again, most existing JS code is imperative style and not OO. No mention how functional code is easier to test? That's a bit weird.

All in all, it's a bad article based on limited views.


> I can write a program with side effects, state mutations, assignments, iterations and conditional statements in OCaml or Scala without issues.

That's only because those languages are not purely functional. If you have state mutations and side effects you're not doing FP at all. Just because a language is known as a "functional language" doesn't mean anything and everything you are able to do in that language qualifies as FP.


You're mixing function programming and pure functional programming.

> That's only because those languages are not purely functional. If you have state mutations and side effects you're not doing FP at all.

You can be doing functional programming with state and mutations. Functional programming is about having first-class functions and using them, and often takes the form of function composition and higher-order functions.

> Just because a language is known as a "functional language" doesn't mean anything and everything you are able to do in that language qualifies as FP.

That is true. But you can have an impure codebase and still be doing FP without issues.


> You're mixing function programming and pure functional programming.

I am glad you said "function programming", as in, programming with functions. That's what you're doing. Don't confuse it with Functional Programming!


That was a typo. Please define functional programming, as every source I've seen define it as precisely that, programming with functions.


I see there is a short section about Python (6 resources).

I have been maintaining for the last 5 years a slightly richer collection at: https://github.com/sfermigier/awesome-functional-python/


Forgive me if this sounds ignorant, but who is using pure-functional approaches in industry?

Again, please correct me if I'm wrong since I've been industry for barely 2 years: back in school functional programming looked cool, but when I entered industry(and even in my exploration through personal/OSS projects), I have found it to be nothing more for the most part than a cool toy to play with. I can somewhat see the benefit when it comes to parallelizing Big Data, but that seems to be a very special case.

Again if I'm just being ignorant, please educate me :)


Lots of people take useful parts of the functional paradigm without bringing in the whole shebang. Javascript being at the top of the list of languages in that page is a good hint at how people use it: things like Array.map are often used instead of procedural counterparts because people consider the pipeline-like data flow to be easier to understand than ad-hoc for loops, to give one simple example.

Promises and the async/await syntax sugar on top of them are another JS construct that borrow quite a bit from functional literature.


I see what you mean...Even I myself have written code along the lines of "array.map.filter.this.filter". And indeed it can be more readable certain times. But I guess what I was referring to is that sometimes it seems that people are "preaching" functional programming as gospel. But I could be misinterpreting things.


I mean, you can go all the way to the deep end, but like any tool, there are good reasons to be pragmatic, and avoid just using a tool for its own sake. Sure, you can get lost in fantasy land[0] algebras if you really want to, but generally I see it as a pyramid of usage (i.e. a small number of people that care about the mathematics, a slightly larger group of library authors that use fantasy land and friends to inform API design decisions, and a larger portion of library consumers that benefit from the academic roots of those design decisions.)

[0] https://github.com/fantasyland/fantasy-land


My software team uses Haskell to help build tooling and support around an enterprise ISP environment. It's a shift in thinking from standard imperative/OOP programming, however, what we've gained is confidence in maintaining and refactoring systems that constantly change due to business reasons over the period of years.

Purity helps quite a bit with narrowing down bugs; you simply check that your inputs and outputs are correct without having to ever worry about state. We use effects to narrow the types of functions that can be legally called inside specific domain logic, which also narrows down a large class of bugs that we've seen creep into other code bases.

However, it's not a magic bullet. Time to getting someone unfamiliar with Haskell up to speed can be costly. Discovering time-saving idioms in a sea of bad documentation is frustrating. Smaller ecosystem than other popular languages means less blogs, and in Haskell's particular case the information seems either too low or high level with no in between.

Still, with 250k of production lines running as stable as can be, we have no complaints.


Pure-functional techniques can give advantages 'down the road', regardless of what language they're used in. For example, pure functions can be tested without any mocking; they can trivially be made concurrent/parallel; their contents can be rearranged/refactored (referential transparency); etc.

Unfortunately it's very tempting to intorduce side-effects 'just this once', which can make those techniques less useful, and takes some discipline to avoid. For example, in Scala it's easier to just throw an exception instead of wrapping results in a 'Try' type; or likewise for null/'Option'; etc. mostly since those results then require map/mapN/flatMap/traverse/etc. to handle, rather than giving us values directly.

However, I think it's usually worth the effort. For example, those map/mapN/flatMap/traverse functions are essentially 'setting policies' for how effects should interact; whilst the 'core logic' can remain completely agnostic.

As a very simple example, if we have 'l: List[A]' and 'f: A => Option[B]', we can combine these in multiple ways, e.g.

    l.map(f)     : List[Option[B]]  // Run f on all elements, keeping all results
    l.flatMap(f) : List[B]          // Run f on all elements, discarding empty results
    l.traverse(f): Option[List[B]]  // Run f on elements in sequence; abort if any are empty
We can replace 'Option' with 'Future' to talk about concurrency rather than emptiness. We can replace 'Option' with 'Try' to talk about exceptions rather than emptiness. All of those expressions remain identical.

More esoterically, we can replace 'Option[T]' with 'Reader[X, T]' for dependency injection of an 'X'; etc.

I've used these techniques commercially in PHP, Python, Javascript, Haskell and Scala (and academically in Racket, StandardML, Coq, Idris, and Agda too)


Truly a nit-pick, but referential transparency is used wrongly by basically every FP-language for some reason. What they actually mean there is pureness, not referential transparency - it is pureness that allows the replacement of a function call with its result, not any other feature of a language.

Hell, Java may actually be more referentially transparent than Haskell, due to it not having macros - the one thing that can break this functionality.


I used to work at IOHK which built the Cardano blockchain in Haskell (and even a bit of Agda in there too). It's cool to work with, features like liquid haskell have ruined most languages for me, but it's a real PITA when it comes to 'normal' things like building a CRUD app. At least it was for me. Also the build times can get horrific.


My company is all-in on Haskell. We write a web application for the reinsurance industry. It's working great for us.


I'd like to recommend the following awesome expositions by Ben Lynn as well:

https://crypto.stanford.edu/~blynn/lambda/

https://crypto.stanford.edu/~blynn/compiler/


Too bad Github search doesn't find that entry in the Awesome listing:

https://github.com/sindresorhus/awesome/search?q=functional%...

And Github search should display the rendered markdown so we can click the links.


The number of monad tutorials in the haskell section is ... interesting.

If you order all concepts in prison programming by the product of usefulness and confusion it must be somewhere at the very top.


The concept itself is extremely useful (in a programming language like Haskell, at least), but for whatever reason it resists understanding in the way most people want to teach it.

I'm not sure why it's different than other programming concepts in this way... maybe just because it's such an abstract concept where most people have no frame of reference. You don't appreciate it until you've used it in a bunch of different contexts and finally see why this one abstraction works for all of them


I feel that a certain segment of the Haskell community doesn't help matters by pretending that monads (in the Haskell sense) have something interesting to do with category theory. Fundamentally, a monad (in Haskell) is just a set of operators that obey some simple algebraic laws. These laws can be illustrated with simple examples, and don't require any deep mathematical knowledge to comprehend.

If you really want to, you can use the language of category theory to talk about monads in Haskell. Some people seem to get a great deal of satisfaction out of doing so (and I suppose that it's harmless fun). But it does build up a certain undeserved mystique around monads in Haskell.


Interpreter patterns are always hard to understand, in any paradigm. It's a combination of newcomers having no idea why one would use them, they being completely meta and unrelated to the final problems, and all the explanation available being trivial about how they are constructed or completely handwavy about all the ways one could use it.

It's just that on other paradigms interpreters are a very advanced and completely optional topic. While on pure FP they are fundamental.


> These laws can be illustrated with simple examples, and don't require any deep mathematical knowledge to comprehend.

If you could share some of these examples, or perhaps a link to those examples, I'd greatly appreciate it.


I wrote an explanation for them here: https://news.ycombinator.com/item?id=28844533

Though I’m not the best at explaining things, hopefully it makes sense.


Oh okay. So what I'm understanding from your post is that, a monad is just a way of applying the return value of one function as a parameter for another function.

Sorry if I misunderstood. I've been reading many explanations for monads, but insight seem to elude me.


Well, mostly - but I think this wrapping of things is the more important part, allowing us to manipulate the inner things without actually being able to touch those.

For example, mapping some function over a Result type that may or may not have a value in an iterative manner would make me check for the existence of the value, unpack, and apply function. If we have a Monad abstraction then I can just map my function which will be applied in case of a result, or the whole thing will remain None - it is safe either way. And one other useful part is the uniformity of functions working on monads - I can use the same ones for a List, Result, or any similar container, IO, State monads and even Async is a monad.


The explanation on the Haskell Wiki isn’t too obscure (if you already know Haskell) and has examples:

https://wiki.haskell.org/Monad_laws


Monads are pretty trivial to understand if you learn some Haskell first, and kind of pointless if you don't. The actual definition of a monad in the Haskell standard library is a dozen lines or so of simple code.


Too much JavaScript.


I honestly wish people new to web development would learn Elm before trying to learn the dumpster fire that is the JavaScript ecosystem.


Speaking of which, how learnable is Elm without any HTML familiarity? I more or less gave up on learning it once the (previous) Elm book started piecing together HTML tags in Elm syntax...


It's not any more difficult than writing the markup in any other language or library, but if you're making websites then you're going to need to know how to use HTML.


Stared only to never use it.




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

Search: