Hacker News new | past | comments | ask | show | jobs | submit login
Learn Functional Programming Visually (david-peter.de)
354 points by polyrand 9 months ago | hide | past | favorite | 65 comments

This I don't understand: Seems like whenever functional paradigms are discussed, the whole same stuff is always repeated without failure: on one hand fuzzy generalizations like "pure functions", "no side effects", "no state"... on the other, all examples always talking about using map, filter, fold, flat, zip. One would think that "functional programming" _is_ using those functions. That FP is all about Iterators and what things you can do with them.

But I never see contrived real-world examples, those that really show how things are different from imperative or procedural programming. I want to see how you use stateless functional programming to control a garage door actuator. Or how you model a Car which can have their doors open or closed either by the class itself or external actors. How do you receive UDP packets, including a buffer implementation that is able to sort out-of-order packets and send retransmission requests for missing ones? That kind of stuff.

Are there any resources that show these kind of "here is a list of problems, how they are typically solved, and how you could solve with FP" style examples?

EDIT: This message was actually to say thank you and that TFA is an awesome resource to help understand how the Iterator methods work! And then I got lost in my semi-rant and forgot to actually say thanks :)

I'm currently reading Domain Driven Design Made Functional. https://pragprog.com/titles/swdddf/domain-modeling-made-func...

It shows how a functional approach can embed much of the domain logic into the types used. They are so simply expressed in F# that non programmers can understand what they say about the problem domain.

The book is selling me on the idea that Functional programming could do a better job in CRUD style business apps.

For anyone looking for a smaller introduction to this book, the author Scott Wlaschin has a talk with the same name: https://youtu.be/2JB1_e5wZmU

It doesn't cover a lot of good topics in the book, but it might whet your appetite.

I’ve found Grokking Simplicity [0] to be a great, pragmatic introduction to solving real-world problems with “functional thinking”.

All the code is JavaScript and it tries to walk you through how you might solve common, everyday business problems with a more functional approach. All without getting bogged down with terms like “monad” or “functor”.

This book (along with Domain Driven Design made Functional, mentioned elsewhere) are both great resources for getting introduced to “useful” functional programming.

[0] https://www.manning.com/books/grokking-simplicity

Thanks a lot for the suggestions and comments in this and other sibling comments, I now have for sure a bit of reading and learning to do!

> But I never see contrived real-world examples, those that really show how things are different from imperative or procedural programming. I want to see how you use stateless functional programming to control a garage door actuator.

I'm not sure why you'd want to see contrived examples, but the reason to focus on map, filter etc is that this is where it starts, and this is what functional programming _is_, transforming sequences of values with pure functions, rather than keeping a lot of state and change it imperatively.

How to write a real world program with functional programming is a later question, and pointless until you understood the basics. All your examples also include a bunch of concepts and data that A. can't be purely functional and B. will vary widely between programming languages, so they are very poorly suited for an introduction.

I agree. So, I guess what I mean then is that once you're past the usual introductions, there aren't many "next steps" kind of guides to start doing actual work with the functional mindset.

Although I must say, my examples are actual instances where I tried to put the shallow learning I had done about FP and failed spectacularly at doing so. It was no doubt due to my ignorance and lack of experience with FP, but maybe also there is a part of me not working with problems that lend themselves easily to a FP approach...

You are completely right that FP is lacking in guides that go beyond the basics. There's a great abundance of introductory material, but very little about how functional programmers structure large programs or deal with complex I/O.

That is way too much for me to put into a single comment, but the most common approach is to split your program into "pure" and "impure" parts and then model all of your interactions with the real world in a way where the "pure" part of your program becomes an "interpreter" that deals with the pure representations of real-world events.

A common pattern for doing this (but by no means the only one) is using Free Monads.

I'd argue this is true for most programming paradigms, and even some design processes. There's a lot of "draw the rest of the fucking owl" mindset when it comes to structuring and organizing code.

I guess it's a little easier with OOP, but it isn't very clear to me how "polymorphism" helps me open a garage door, or even achieves it's stated goals. I guess I can at least understand that now I can open any door by calling door.open() or something. Of course it gets much worse when you graduate and people tell you OPP is overhyped, there's too much abstraction here. Then you think "I thought the abstraction was good". Don't just create a garage door, create a door factory! Then you realize you don't have a good defense, and that you don't understand as much as you thought. You were never taught to design programs pragmatically. You just have to figure that part out on your own.

I can understand how mutable state causes problems and would be a good thing to avoid where possible. There's really not much needed to convince even a moderately experienced programmer, of the power of pure functions. It doesn't feel that far off from the OOP example.

The next step here may be recognising that just as you'd map a list, you can also map lots of other data structures, namely anything that's a functor (see: `fmap`).

Have something nullable? Stick it inside `Maybe` and map it.

Have a side effect in `IO` and want to manipulate the result? Map it.

Following that, look at monadic binding. It's basically flat mapping.

From here you can start to see how programs can be composed in languages like Haskell. You have these foundational typeclasses and you sort of just connect all your functions together with them until you're left with one big expression in `main` which is your application entrypoint.

I suppose it’s tricky to apply an FP approach when you get closer to the external interface, it’s moe suitable for the more internal stuff. But when you combine it with reactive approaches into FRP you can do more, and there are more complex examples to be found.

But I’d say in general that FP is mostly just an alternative to OOP, maybe even leas than that, not a complete paradigm for writing complex programs. At least if you use a mainstream language, then FP is probably something you do pretty locally.

FP can be used for the same solution, for which OOP would be used. In many (if not all) cases, the FP solution would be less complex.

The only caveat is in I/O mechanisms. An IO monad is one way to safely manage IO, but it is not the only way.

I’ve long struggled to “get” functional programming - both its syntax, and it’s real world uses. Everything is so terse and abstract and I find the lack of clear expressiveness to be daunting. It all often feels too “magic” to me which makes me suspicious. Then I wonder “what’s in it for me?” - how does it make my job or life easier? (I find myself much less motivated to learn something new without a clear answer to these things.)

One things I’ve found enlightening is when participating in Advent of Code exercises, watching how people are able to solve problems using functional languages. While I may not always grok the solutions in their entirety I’m often impressed by the compactness and elegance.

I share your experience. What worked for me and enabled me to grok FP was using Linq in C# (using the extension-method syntax, not the keyword-based syntax). It started-off with me wanting to avoid typing “foreach” everywhere when processing lists/collections/etc and then it all followed from there.

My only complaint about FP, and Linq in particular, is that it can often result in suboptimal execution when you have preconditions that Linq isn’t aware-of, such as a known input size, or known uniqueness of some key-property - and there isn’t a way to supply hints. And C#’s weak support for contravariance, aieee. Still lightyears ahead of Java though :)

All of the real world examples you described above involve I/O. There are various approaches for this, the most popular being the IO monad.

A function using the IO Monad looks like regular imperative code except for the type signature of the function which indicates that the function side effects and thus can only be called by other functions that side effect. Pure functions cannot call a side effecting function. This helps in isolating side effects and capturing it in the type signature of functions.

Plenty of examples here


Functional programming is not stateless programming. Rather it captures any state changes in the type signature. An explicit example of it would be the state monad.

Many functional languages do not model effects in the type system, like the ML family. Many do not have static types at all, like Clojure/LISP.

Those languages wouldn't be considered pure/stateless as intended by the OP.

Haskell would not be considered pure or stateless as well. What the OP is saying there are several qualities which indicate you are dealing with functional language and they are not super rigid. Typically first-class functions indicate that its possible to do things functionally. Immutable defaults takes it further. Higher kinded types usually indicate you can easily separate pure from effectful. Dependant types hint that you can have value level expectations etc.

At which point you are happy with "functional" is up to you. Its not all or none.

I share your opinion. I found the book Real World Haskell very useful http://book.realworldhaskell.org/ It has a few nice chapters about system programming, database interactions and a few other use cases.

Here is an educational bulletin board website built with Haskell: https://gitlab.com/gilmi/bulletin-app

Redux in the react world is a good example of real-world application using pure functions and functional programming concepts.

Likewise most of the hooks (useReducer being the obvious primary example), and functional components in general. That's how 99% of functional programming is done - state and function are separate, first class, concepts. The line is very distinct.

I don’t thinks hooks are a great example. Hooks are by definition effectful. Best compared to ‘Applicative’s in Haskell. (Not monadic unfortunately)

So a functional component in react using hooks is not pure. You can translate it into FP with a bit of magic, but requires a context. The line is not that distinct at all.

Hooks themselves may not be good FP examples, however I meant the resultant functional components that you develop yourself usually are.

Hooks are implicitly injecting whatever global state in your stateless components, so definitely not a good example of functional concepts imho.

One of the main differences in reasoning I think is how each paradigm deals with time, not in a datetime or library sense, but in the literal sense of time and causality. Does the paradigm abstract the passage of time or not? FP abstracts away time, and statements in FP are timeless, true now and in the future. That is why it ends up looking like math a bit more. Once statements in the language are sensitive to time... that means there is mutation and the line above can have a side effect on lines of code below. It's like FP is jumping a dimension and it makes statements that have to be true at all times (pure FP anyway), while mutation languages give you fine-grain control on what is being computed each unit of time.

Of course you then have a mixture of both, which many if not most modern languages have. Pure FP can be expressed in a language that supports mutation anyway, it's just a matter of how much first class support those features have. Languages like Haskell I think go so far into the abstracting of time, that they reached around and achieve something like mutation based syntax, except there is a tower of abstractions working under the hood, ensuring that it's still timeless equations which result in side effects.

What makes you think that a person with a degree in theoretical physics who is making their livelihood writing code is qualified to teach functional programming and more so address the (well justified) peculiarities of FP you are raising here?

I think you are replying to misleading title and some other article. There is no "TFA" here; it's a little game.

Iterators with a capital I are not functional.

Functions like ‘map’, and ‘fold’, and ‘zip’ are; but Iterator is not the term to describe such function.

Truth be told, me writing Iterators with a capital I comes from when I finally had this "click in my mind" moment where I got to see clearly how map & friends work, by using Rust [1] to solve a bunch of Advent of Code 2019 problems

[1]: https://doc.rust-lang.org/std/iter/trait.Iterator.html

Cool game!

Headline here might be a little grandiose. Author has "A puzzle game inspired by functional programming" on the project's Github. I find this more apt

This reminds me of the tree diagrams I wrote during the Term Rewriting Systems course at the Vrije Universiteit [1].

Functional programming really started to resonate with me at that time. Properties like confluence were very useful to understand distributed systems algorithms later on, and e.g. how eventual consistency plays out.

[1] The teacher was awesome too. If you'd like to learn more about term rewriting systems, go take a peek at his slides http://joerg.endrullis.de/teaching/#trs (Note, just noticed that they are behind a password. A gentle email will probably get you the slides though :)

Fun to see you here! We've studied together! I'm glad I'm not the only one recommending Vrije Universiteit courses on HN, now we're a crowd :P

I'll send you a PM :)

Cool game, but I don't think it is actually a good idea to see this as "learning functional programming".

After all, this shows some very convoluted ways of getting a simple end result ;-)

Also, I've only played the first few easy levels, so I guess there will be more than "map" later on? Otherwise I would just leave the "map" word out of it.

No, it's just a misleading HN title.

As a game it seems to exercise the same parts of your brain.

The biggest challenge with teaching functional programming is that people tend to take the word function or the concept way to seriously. They start mandating restrictions on input/output, what functions should be used for, and all kinds of other unnecessary nonsense. It’s like they need some sort of self imposed guardrails to qualify making better or more common use of functions. Like they need an official invitation before joining the party.

Perhaps some of that stupidity is bound to a developer’s understanding of their language at hand. Some languages are more expensive than others. This is where the sad confused developer mandates that functional programming must be declarative... until you point out the languages Red and Rebol are functional imperative languages. It’s not some shallow wishful opinion. That is how those languages describe themselves and it’s what they look like.

When you take all the stupidity, assumptions, and restrictions away functions are most universally a bag of instructions, as are classes and various other things. Functions are unique from other instruction bags in that they execute. That creates potential for instruction reuse and thus portability.

Different languages impose different restrictions on where or how functions may present. In JavaScript functions are first class citizens which means they can be used anywhere a primitive may be used, which is almost everywhere. Functions can also be nested in that language which just means subdivided layers of portability/reuse. In this case, in a highly imperative way, functional programming can mean programming with a mindset of functions first, as opposed to other means of structure or extension. Again that interpretation of functional programming tends to make people uncomfortable as there are no restrictive guardrails or super specific defining rules.

> They start mandating restrictions on input/output, what functions should be used for

Like what? You've thrown out a ton of generalisations, but nothing here is concrete.

> When you take all the stupidity, assumptions, and restrictions away functions are most universally a bag of instructions, as are classes and various other things.

If you group all things together as a 'bag of instructions' then there's no point in teaching functional concepts or any paradigm's concepts for that matter.

I am reading between the lines here, but I am assuming by 'restrictions' you're referring to functions taking immutable values and returning immutable values. There are sensible reasons for this (along with referential transparency in general) that enable function composition, which is the 'super power' of functional programming. It isn't a requirement, it's just good practice, and so I can understand why FP advocates would promote it (I certainly do).

A class can have those powers too if it is immutable, because ultimately any class is just a set of functions with an implicit 'this' argument, but it's often understood differently. It doesn't seem unreasonable to promote and talk about the differences in approach, especially in languages that allow you to 'cheat'.

Yes, the narrow definition of a functional language is that it has first class functions. Clearly though there's techniques for success within the paradigm.

Again, it's quite hard to latch onto exactly what you're saying here, but I don't think functions as structure is the argument for FP (as such), it's functions that work like mathematical functions, to leverage the centuries of knowledge of mathematical expressions, proofs, etc. Personally, I find my functional code much easier to understand, compose, test, and generally have confidence in. When I compose two functions I don't have to 'go and look inside' to see what it does first, because they're pure and declarative.

The declarative nature of the functions is an artefact of following the good practice of immutable values, referential transparency, and total functions which lead to 'honest' type-signatures, it just happens on its own.

I spent a reasonable part of my career in imperative-land, and honestly I wish I'd found functional programming much earlier. I write fewer bugs and I have more confidence in my code. And yes, it adds constraints that are annoying sometimes, but those constraints ultimately stop the code becoming a cognitively difficult mass of mutation or hidden complexity.

> You've thrown out a ton of generalisations, but nothing here is concrete.

That was intentional. Specifics aren't necessary for making the point. Had I stated the specifics you were looking for I suspect you would have focused only on that, missing the primary point.

> …missing the primary point.

Like GP, I would have to guess what your point actually is.

They start mandating restrictions on input/output, what functions should be used for, and all kinds of other unnecessary nonsense.

A specific example of what you mean would be great. More comments quoting yourself and refusing to explain what you mean would be not great.

I provided several examples in the last paragraph of my original comment.

Whether or not you're right about functional programming, your use of "stupidity" made me think more about what kind of person you are rather than the merits of your argument.

In your other reply you come off as haughty or patronizing.

One does not need high EQ or SQ to be kind.

> That is how those languages describe themselves and it’s what they look like

Neither Red [1] nor Rebol [2] actually does that; the Rebol family of languages tends to describe itself as paradigm-neutral or multi-paradigm, but historically stays close to imperative constructs and lacks mainstream FP support (e.g. Red doesn't have closures, currying, and tail-call optimization). If you really look at a generic Rebol code it will look like an Algol 60 ocean with occasional DSL islands floating around.

Each time I ask what "functional imperative" buzzword actually means no one can give me a straight answer. If it means "building imperative constructs on top of recursive functions via macros" [3], then it describes something totally remote from Rebol. Care to elaborate?

[1]: https://www.red-lang.org/p/about.html

[2]: http://www.rebol.com/docs/core23/rebolcore-1.html

[3]: https://bitwalker.org/posts/2018-03-18-functional-imperative...

Good idea but I was able to solve most of the puzzles without ever thinking about what my program was actually doing, especially in the "math" levels. In the sense that I knew what was going on, I got the concepts but I never "evaluated" the program in my head. I just made some educated guesses based on what I thought the patterns might turn into. Maybe that's deliberate here but I thought that's usually not what puzzle games want.

The other problem is that it's easier to brute force the small set of permutations of options, than to think it through.

Since the model is an array of pillars, the visualization has a wall is confusing. Would be better to add some space between columns, and remove the 3D/isometric effect.

The mathematical problems break the "visual" model. They are harder to understand when you have to spend most of your energy doing 8 simultaneous mental conversions to base 8 while solving.

I love it. It goes to show why I love/hate Haskell. . Im pretty sure most of you just look at signatures(colors) only, further I think most of you just tried a couple of different options until "type" clicks.

It is pretty evident that during this you involve yourself in code puzzle / code golf. Rather than understanding the problem at hand. This is fine - until some of your "low-level" stuff leaks or your understanding of it leaks. There is also third possibility for failure - trusting the system too much, despite types being expressive they hide away a lot of "internal" logic, sometimes something looks the same, but it's not and type system doesn't capture it.

I had fun playing through all the challenges. But I don't think I walked away with a better idea of functional programming. This is closer to "learn LINQ", seeing as we're only chaining functions.

I would be interested in seeing this expanded to include passing functions to functions (that is, make the user build a tree of functions, not a list). And yes, that might put you into hot water regarding how to communicate signatures and such.

The way this works technically is really great. Kudos to the developer. The game itself ... I do not think it helped me learn anything.

This game reminds me of Infinifactory by Zachtronics https://store.steampowered.com/app/300570/Infinifactory/

Really cool! If you had the time/energy, this would be an enjoyable mobile game.

I don't understand 'stack'. What's it doing, exactly?

It adds a new layer of cubes of the specified colour.

I agree this game is a bit hard because the “type” of each element passed through the functions isn’t documented (is it a cube? A column? How is a 2D shape’s cubes iterated? Does order matter?)

Welcome to Haskell :) Just 'Align the types' :)

Ha, ok. I was making this more complicated than it was.

This is cool. It would be improved by adding the CSS #controls { user-select: none } (with various vendor prefixes). A bit of refinement to how dragging is detected wouldn't hurt either.

In case you are looking for the answers -> https://pastebin.com/tbrJreNn

This is really cool but I am not sure it helps a person devising a plan towards the solution. It's more of an exploratory stumble-upon-solution activity.

What about Excel ? ;)

Looks useful but I wish it fit on the screen on mobile! Just gotta change the width with media queries

This was an overly complicated example to learn what functional programming is.

What is the programming equivalent of stack equal columns?

I get the map and filter operations but I can’t remember ever coming across a function like that before.

> What is the programming equivalent of stack equal columns?

The game's "stack equal columns" corresponds to Haskell's Data.List.group function:

> import Data.List

> group [1,2,2,3,3,3,2,1,1]


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