Structured Programming meets Functional Programming
For a little bit now, I've been practicing a simplified version of Functional Programming in F...
For further actions, you may consider blocking this person and/or reporting abuse
Do you (want to) use state machines to describe UI? I've been using the Elm-inspired MVU JavaScript mini framework hyperapp for a while but also use xState for UI state management. Is a wonderful combination.
We have been writing front-end UIs in F# and using Elmish for the MVU details. Thanks for mentioning those. I will check them out. Nice connection between MVU and state machines.
Thanks Kasey, it was a great read!
I've noticed how I adopt a vaguely similar style with Python: composable testable pure functions as much as possible, isolated functions with side effects, the module as a unit of separation and encapsulation, no private (which doesn't really exist in the first place).
The big thing missing from my "functional style" in Python is recursion since it doesn't do tail call optimizations.
That's great to hear! I figured I couldn't be the only one who arrived at this style, regardless of language.
That actually brings up a good point that I forgot to mention -- why FP uses recursion for looping. It is because recursion allows you to loop without using mutations. Whereas
for
andwhile
loops use mutable variables to control looping.However, think about this: TCO turns the "pure" recursive loop into a functionally-equivalent imperative
while
loop. So it must be that mutation isn't so bad. And in fact, a function can use mutation and still be deterministic as long as the mutations are local. Meaning only values declared inside the function are mutated -- nothing from outside.So anyway, recursion does help looping to be more declarative, but it isn't strictly necessary with a little discipline instead. :)
Yes, though it's not always the case in reality, many for loops have side effect in imperative programs :D
An interesting example of an improvement: Go loop variables are scoped within the loop, you can't access them from outside the loop.
For sure, it is otherwise normal to perform external side effects in for loops. Just to say that with some discipline it can be avoided. Though I think that way of doing things is pretty foreign to most of us.
This sounds very cool! Got any examples of this?
Learning Category Theory is considered a prerequisite in much of the functional programming material I have seen online.
I think this is a subset of why getting FP out to the masses has been so difficult - the overly academic nature of the core FP community. Whenever I see Fibonacci or worse, Euler, in an example I just want to weep.
I've never once been paid to write a program that generates a Fibonacci sequence, but I have been paid to write a program that takes some input from a user, does something with that input and outputs the result to a database (or RESTfull service these days) and then does the reverse. I'd like to see more articles out there about how FP can help me with that - preferably without mentioning monads once (which seems to be a challenge with FP and I/O).
I agree with you. FP was hard for me to approach even in F#, which doesn't have category theory abstractions built-in. This is sortof the missing manual I wish I had when learning F#.
I use FP for, to borrow again from Scott Wlaschin, BLOBAs or Boring Line of Business Applications. No fancy scientific or math-focused work, just standard business web apps. And it is has been a huge maintainability improvement over previous efforts in VB, C#, and Javascript.
If you haven't already, you should definitely check out MVU. Elm probably has the best beginner docs out there, but I prefer to use F# Elmish nowadays. MVU has a way to handle/isolate side effects that doesn't require hearing the M word. I am working on an article about adapting that to server-side processes as well.
"...previous efforts in VB, C#, and Javascript." -- I'm curious what you did with the JavaScript portion? I've haven't found F# creep into that arena for me yet. It seems very SPA driven and I find BLOBA's best fulfilled by hybrid apps.
Our apps were initially desktop VB apps. Then server-side web apps using WebForms. These sprang up organically over time and often there would be overlap in functionality, which lead to code duplication with subtle differences and heisenbugs. So we started to try to centralize the logic into an API. We also at times had some perf problems due to WebForms regenerating the whole page on every interaction. Between that and WebForms kindof hiding the HTML from us in aspx tags, which meant we had to reverse engineer things like how to style elements, we wanted more direct control over the HTML. SPAs were catching fire at that time, so we migrated to using the front-end/back-end division for our web apps. Which moved us more into the Javascript world for front end. We used jQuery and Angular v1 for a while, then Elm, and now F# (Fable Elmish) for front end apps. For the back-end we used C#, and now F#.
Most of what I described above is for our custom internal system. But we have moved more toward creating cloud-based products, which we also happen to use for ourselves.
Very cool. On the server, are you guys using giraffe? Suave? Freya? MVC?
Started with Giraffe. They changed the API to chase performance, and I didn't really like where it landed. I experimented with my own over a weekend and that went well enough that we use it in our products. repo link
I really loved this article. It is very similar to how I have started using F# in my work. It's nice to know I'm not alone! I have been using
private
but was considering moving them to sub modules as it didn't feel right. I'll definitely be doing that now!Do you have any recommendations for handling side-effects? F# coding conventions (docs.microsoft.com/en-us/dotnet/fs...) recommend using classes but I don't really want to go that route as I prefer to show that my code is using FP style. However, initialising a random number generator, for example, is not thread-safe when instantiated statically:
My thinking is to initialise it at the start of the app and pass it in to any functions that need it, pretty much how to manage any kind of state in FP but I'm still learning this stuff.
Thanks again for a great article.
If it is something that can be initialized once, I often do it at the start of the program as you mentioned. Basically, the entry point to the program can do whatever it needs (including side effects) to setup the program.
I am currently working on an article for how we are managing side effects interleaved with logical decisions. Here is an extended example that I plan to explain in the article. It is based on the MVU pattern. And in fact, for the front-end we also augment the MVU pattern with a Perform function to run side effects. Example here.
For randomness, I have an old article that addresses how to shuffle without side effects.
I also highly recommend Mark Seemann's blog for articles on functional architecture. Here is a good one, for example.
Thank you for this and thanks for those links. I'll definitely check them out.
Love the shuffle idea.
I wrote a followup article for how we are handling side effects on the back-end.
Testable Back-end Programming in F#
Kasey Speakman ・ Apr 8 ・ 11 min read
Great article! This paragraph stood out to me.
I'm eager to see your strategy that helps with this.
Thanks!
Sorry for the delay in writing that article. There are two main reasons. We've been heads down working on a new product. Also, I am using the product as an opportunity to refine my approach before publishing. I like to have the code in production and face some of those edge cases before I write about it.
If you are not familiar with the Model-View-Update pattern, then the rest of this comment probably won't make sense to you. And I would encourage you to check it out instead of reading further.
I can sum up the essence of the approach. It is an adaption of the MVU pattern (aka The Elm Architecture). The main modification to the MVU approach is that I create another type
Effect
(a DU) to represent side effects, and a functionperform
to execute the effects. TheEffect
type should be immutable with value equality for testability. Then theupdate
statement will return aModel * Effect list
instead of whatever it normally returns. This is easily adaptable to work with the Elmish library on front-end projects. On the back-end, there is no need for me to run the Elmish library or bother with aview
function. Instead, I essentially plug the same MVU (sans V) types and functions into a small recursive function that runsupdate
andperform
until there are no more messages or effects to process.There is a trade-off obviously. The main downside is that I have to always define types and functions associated with MVU. (But on the plus side, I always have a place to start.) The other downside is mentally transitioning from interspersing side effects into the update/perform structure. In the article, I plan to cover some guidelines/lessons-learned.
The benefits are pretty strong. Using the
Effect
variation, theupdate
statement can be completely deterministic. Not just for theModel
, but also for theEffect
s. Put another way: not only can you test that the logic produces the correct answer, but also that the correct side-effects are invoked! Without any sort of mocking framework... only using an equality comparison. This isn't even available in Elm since its effects (Cmd
) are not equatable. And of course, having a deterministicupdate
statement makes it much easier to refactor safely.That is the basic run-down. Hopefully if you are familiar with MVU, it made some sense.
I wrote the followup article.
Testable Back-end Programming in F#
Kasey Speakman ・ Apr 8 ・ 11 min read
We have being implementing solutions for 3-4 years now in f# where we have adopted a "functional style" approach and we do alot of what you have described above and more. We dont implement classes (unless we have to), we use modules with grouped related functions that are glued together in workflows (which are themselves just functions.
We place full emphasis on type driven development where sum types (i.e. discriminated unions) and product types (i.e. tuples) play an integral role to enable us to express the intent of the code. Functional patterns such as partial application, function composition and pipelines are things we lean upon heavily.
Lastly we really emphasize on separating pure functions from functions that contain side effects, we have played quite a bit with category theory and we are using some monadic composition in some places albeit not substantially.
If I'm honest I'm heavily influenced by the likes of Haskell, powerful type system with emphasis on separating out side effects on their own. The struggle for us for quite a while was understanding how to construct actual production ready applications e.g. how do you manage dependencies in an elegant way, and how do we test functions that contain dependencies etc, in particular when there really isn't much out there for developing f# applications like I am describing. In truth, we have learned by looking at other functional language ecosystems.
Very refreshing read to see others thinking along the same lines as we currently are, thanks for sharing
Really enjoy this posting that gives the gists of functional programming, can not be simpler. I got a feeling that anyone with C/Java/C# programming experience can learn FSharp in 30 minutes by reading a blog like this.
An amazing summary.
Great F# intro, what the Tour of F# on MSDN should've been.
int Add(int a, int b) => a + b;
This expression bodied syntax in C# is only available if the method is a single statement. Once you go beyond a single statement, you have to use curly braces and
return
. It’s a handy shortcut when you can use it, but it is not the typical C# experience. The F# code could be shorter as well:let add = (+)
However, the example in the article is demonstrating the structure that you will usually see in each language, not the shortest possible representation of that specific code.
Okay.
Another beauty Kasey. Thanks for sharing.