8 ms·
Application-as-a-Function Thinking
- 12thwonder 4y agoit seems like less sophisticated version of reactive paradigm to me, am I wrong?
- AugustoCAS 4y agoI think you are right. If the response would be a promise or similar, it would become a reactive system. Disclaimer, I've avoided using reactive approaches as much as I can on the server side as it increases the complexity (and cognitive load) and makes diagnosing issues harder. This is my experience in the JVM world, I'm not sure how it is in other languages/platforms. [This article](https://netflixtechblog.com/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c https://netflixtechblog.com/zuul-2-the-netflix-journey-to-as...) from Netflix mentions the tradeoffs they experienced when they re-engineered their API gateway to be reactive. I wonder if this is still valid as the article was written 6 years ago.
- the_gipsy 4y agoReactive.js is hellish too. Elm (basically The Article) dropped it too in favor of this simpler approach, but keeping some interesting parts.
- kodroid 4y agoI guess it depends what exactly you mean by "reactive paradigm". In my experience the commonality found between client side mobile applications and reactive principles is essentially everything is a stream, and the UI subscribes to those streams. The approach outlined in the post is similar in terms of the UI observes a (single) stream of data, which can just be a simple (State) -> Unit) as opposed to some reactive library stream primitive. The approach outlines is different from all the "reactive" client side applications I have seen as there is no proliferation of stream primitives present throughout the codebase, which often seems to create complex code with very little if any user benefit. I'm not sure what other elements you see in the post which also fall into the "reactive paradigm"?
- AugustoCAS 4y agoI couple of years ago I was lucky to use http4k, a server as a function web library for Kotlin. It was such a wonderful change compared to every other technologies available in both Java and Kotlin. It's simple. Testing becomes so much easier too, as one can instantiate a the whole web routing aspect, without having to bind it to a port and having to send real http requests. If strongly suggest people to take a look at it. It's not perfect, but it's a lot simpler than other frameworks and libraries. And it's a shift in some of the current mentality of using heavy frameworks (such as spring boot) which blow up anyone's cognitive load. https://github.com/http4k/http4k https://github.com/http4k/http4k
- discodachshund 4y agoScala's http4s is also such a lovely framework. Given the name similarity I don't know which came first
- idlehand 4y agoThe problem is that most Java developers like the cognitive complexity that Java EE frameworks entail. Those who like simplicity generally leave Java behind.
- kodroid 4y agoThanks for pointing this out, I had not heard of this one and yes sounds like it shares very similar ideals but for the BE.
- jjice 4y agoSounds kind of like Go's HTTP handlers just being functions. Makes testing them very easy since you just call them with an http.ResponseWriter and a http.Request struct.
- deepsun 4y agoNot sure what's new here. Handlers were always functions in java (and other languages), even 20 years ago. PP talks about general setup of the server in http4k (which I'm not a fan actually, because server setup is a very small thing done once. Just as a build file, I actually want it as long and verbose as possible).
- iddan 4y agoThis is basically introducing Elm (a.k.a the inspiration for Redux) to Android. At least for me this model doesn’t work. Application is all about transitions of state, yes, but in context. That’s why React’s model of managing multiple state points in different levels of the application tree makes so much more sense to me
- Huh1337 4y agoElm was inspired by Flux/Redux, not the other way around.
- iddan 4y agoSorry no, the redux docs specifically mention Elm as an inspiration and previous art https://redux.js.org/understanding/history-and-design/prior-art https://redux.js.org/understanding/history-and-design/prior-... Flux was before, but it’s not about application as a function
- Huh1337 4y agoHmm, you're right, Redux first appeared in 2015 and Elm in 2012. I was early adopter of both (didn't stick with Elm though) but I guess I only noticed Elm once it got popular. Thanks, my mistake!
- dminuoso 4y agoNeither is the case. Elm semantics were originally loosely based on functional reactive programming in the sense of Conal Elliot, as per the thesis of its author. Redux on the other hand is just imperative programming folks rediscovering that a state transition can be described as a pure function S -> S, with all the benefits that come along with it. This is as old as lambda calculus itself. Words like reducer are just red herrings.
- the_gipsy 4y ago"in context", what does this mean?
- raydiatian 4y ago> “Application as a Function” Forgive me if I’m oversimplifying, but isn’t this just “stateless servers” and “router-service separation” at its core? Maybe this isn’t targeted for the API crowd, but anybody who has spent even a few months learning modern client-server programming understands this, and web dev is pretty common these days…
- arvonle 4y agoOr the Unix vision of composable programs that do one thing and do it well. History repeats itself.
- Smaug123 4y agoI think that's orthogonal. One can compose a mutable ball of mud out of a collection of components that individually do one thing well. A more apt analogy, though rather unbelievable (demonstrating how orthogonal the concepts are!), might be a Unix-style system where the entire environment were specified as inputs to every shell command. Nix takes steps towards this, but it's very different in style.
- deleted 4y ago[deleted]
- kodroid 4y agoHey, Not targeted at the API crowd really no, more client side applications, but it seems similar principles have been applied in various places / frameworks as spoken about in other comments here. > Maybe this isn’t targeted for the API crowd, but anybody who has spent even a few months learning modern client-server programming understands this, and web dev is pretty common these days Yes, I'm sure your right, but there are a huge number of people who are not web devs who have not been exposed to these fundamental principles and how they may apply in other operating environments :)
- stevan 4y agoI like to think of it as "application-as-functional-specification". A functional specification for a stateful system is a function from a list of all inputs to an output, i.e. `fun spec(inputs: List(Input)): Output`. This kind of specification doesn't need to specify `State` at all, as we can simply refer to previous inputs. E.g. imagine a counter system with the inputs `Increment`, `Reset`, and `Get`. The functional spec for `Get` is "find the latest reset and then count the number of `Increment`s since then". I think this is neat, because if you are developing the functional spec with a domain expert that doesn't know programming you don't wanna bother them with, for them, unnecessary bookkeeping details. From this spec you can then figure out what `State` needs to be, but this can be done as a separate step not involving the domain expert but rather other developers. I learnt this from the Cleanroom software engineering people, they call the spec without `State` a "black box spec", while the one with `State` is called a "state box spec". (There's also a third step called "clear box spec" where they break down the "state box spec" into functions.) I've been playing around with the idea of designing a specification language that lets you write "black box specs" together with some basic sanity checks of those specs, because I think there's a lot of value in this sort of structure. For example even if your application doesn't follow the "application-as-function" pattern, you can still use your "state box spec" as an oracle when doing property-based testing of your application.
- iddan 4y agoThat idea sounds great. I think this kind of things really work well only in a dedicated DSL
- masklinn 4y agoIt’s not that it works only in a dedicated DSL, it’s that if your language is too general it’s too easy to break out of the architecture when it’s a bother. So the pattern works, but you have to be religious about it, any breach will bring the entire thing down. Hence much easier if the language itself precludes breaking out of the pattern.
- kodroid 4y agoHey, thanks for the input. > This kind of specification doesn't need to specify `State` at all, as we can simply refer to previous inputs. E.g. imagine a counter system with the inputs `Increment`, `Reset`, and `Get`. The functional spec for `Get` is "find the latest reset and then count the number of `Increment`s since then". I could be misunderstanding you, but this does not sound functional to me as `spec` seems to have access to previous inputs to `spec` invocations. What am I missing here? > I think this is neat, because if you are developing the functional spec with a domain expert that doesn't know programming you don't wanna bother them with, for them, unnecessary bookkeeping details. This sounds very similar to DDD (like as described as the DDD book at the end of the blog post). > I learnt this from the Cleanroom software engineering people At IBM direct, or are there any particular resources you can point to which you found useful? Many thanks for the contribution.
- _dain_ 4y agoHow well does this approach scale for systems where the state is inherently large and complex? I write code that controls industrial machinery. There's just loads of different knobs and settings and modes that I can control with code, and a single production or test run will involve several heterogeneous machines, so the total state space is the cartesian product of all of those (not to mention the state of the physical widget they're all acting on, which we can only know imperfectly). I'm trying to imagine writing a data structure to represent all the different configurations the system can exist in, and it would need hundreds of different arguments just to instantiate it. The function to compute one state to another would be enormous and inscrutable. How do you deal with that?
- kodroid 4y agoHey, I can't say I have ever worked on such a project, it sounds like an interesting problem space. I am assuming when a user interacts with one control button / dial / interaction it has a cascade effect on other controls / displays etc? I am also assuming you already need to have a data structure representing the complete state of the system no? There are multiple interesting aspects here, like are you / do you need to utilise complex state machines here? Just the subject of imperative vs functional state-machines is interesting and a small part of the wider approach. Another interesting area is it depends on the language and execution environment you are operating in and how expensive operations over immutable data structures are. Lots of questions!
- _dain_ 4y ago>I am assuming when a user interacts with one control button / dial / interaction it has a cascade effect on other controls / displays etc? Sometimes yes, it depends on the instrument. Some have user interaction, but most don't and are just controlled by the software I write. When I said "knobs" I didn't mean literal knobs, just all the different things these gizmos can do .. they come with thousand-page manuals. OTOH sometimes shit breaks and you have to go in and send commands manually over telnet to unfuck it, or to gracefully shut down the operation without some delicate and expensive piece of equipment getting torn to pieces. >I am also assuming you already need to have a data structure representing the complete state of the system no? Nope, it's only feasible to keep track of the most salient variables. The code is very imperative: machine_a.do_this(), machine_b.do_that(), etc, sometimes for hundreds of lines. So the state changes in the background but it's not legible to us. The machines themselves probably have some representation of their own state in their firmware, but there isn't a single omniscient data structure for the whole system. >There are multiple interesting aspects here, like are you / do you need to utilise complex state machines here? Just the subject of imperative vs functional state-machines is interesting and a small part of the wider approach. Yeah we use finite state machines to organize our code, but it's very coarse grained. Stuff like: LoadingState, ArmatureIsMovingState, TakingMeasurementState, etc. Each state in itself will issue many different commands to different machines. It isn't really formally defined where the boundaries are, it's just an informal way to orient ourselves so we can draw a flowchart and not lose our minds from the complexity, and so that there's some way of comparing similar-but-different programs (e.g. a run that measures current and a run that measures Young's modulus will both go in a TakingMeasurementState, but obviously the commands sent will be different). It's not very amenable to automated unit testing ... I'm not sure what that would even mean in this context. The correct behavior you're testing against would just be ... a sequence of commands to the machines. Which is what the code does already. A unit test would be tautological. >Another interesting area is it depends on the language and execution environment you are operating in and how expensive operations over immutable data structures are. We're using Python. I know that pyresistent exists, but honestly we could probably get away with doing naive deepcopies for every single operation and it wouldn't matter for performance. The runtime is completely dominated by the latency of waiting for the machines to do physical stuff (no concurrency for the machine-controller code thankfully; we're not insane). Immutable data structures don't really matter if we can't write down the entire state in the first place. ---- I guess the problem is that pretty much everything interesting that the code does is a side-effect. There are a few things here and there that can be factored out to pure functions and tested, but the overwhelming bulk is the "imperative shell". It's like one of those planets where the core has cooled and the crust has frozen down to comprise most of the total mass.
- erwinh 4y agoIn a way for me smart contract blockchains are a good example of developers focussing on creating very efficient functional building blocks. A smart contract has 'functional' aspects in that it produces operations that update the state/storage. Developers are incentivised to think about direct cost due to gas/storage fees. Chain-level transaction standards create a base-level of interoperability. All of this being on-chain basically gives 'event logging' out of the box so all functions (i.e. contracts) on a chain can be monitored by any developer involved. (Building this part myself: https://thestackreport.xyz/dashboards/tezos https://thestackreport.xyz/dashboards/tezos)
- throwaway5959 4y agoWhat?
- throw827474737 4y agoPlease just don't.. wtf > event logging' out of the box so all functions (i.e. contracts) on a chain can be monitored by any developer involved You cannot be serious..
- mpweiher 4y agoNo. Applications are not functions. How many applications that you use on a daily basis work as follows: 1. You prepare some parameters 2. You start the application with those parameters. 3. The application goes away and thinks for a bit. 4. The application returns with a result and then exits. Trying to make actual applications and system fit into the function (or procedure) mold is, IMHO, one of the biggest obstacles to software simplicity, as there is a fundamental architectural mismatch here. In the early days of computing, a lot of programs actually did work this way, which is why DSLs for algorithms (ALGOL) were appropriate, and probably where the idea originated that they are actually general purpose languages. Which they are not.
- greymalik 4y ago> How many applications that you use on a daily basis work as follows… It is actually my full time job to support a suite of applications that do exactly this.
- mpweiher 4y agoYep, and these applications certainly do still exist. Scientific applications come to mind, probably also engineering support, and maybe financial modelling? However, whereas they used to make up the overwhelming majority of programs, they are now minority, yet we still try to model all programs like this. Which is obviously possible, after all we're doing it. But it's not a good idea.
- brap 4y agoDid you read the post or just the title? It doesn't say that applications are functions.
- mpweiher 4y agoTFA: Fundamentally an application can be written to have at it’s core, a single, stateless pure function, behold!
- 4y ago
- brap 4y agoVery cool to see this! I've actually given this some thought a few years back when attempting to create (yet another) JS framework, and came up with something very similar to this! For me the difficult part was where the side effects fit in and how they're processed, what you call "commands" and "command handler". I didn't find an elegant solution to this and abandoned the idea early on, sparing the JS community.
- kodroid 4y agoGreat thanks. > For me the difficult part was where the side effects fit in and how they're processed, what you call "commands" and "command handler" Do you mean difficult for you when designing, or difficult to understand in my post?
- brap 4y agoI meant for me, but also in the article I felt like there should be a bit more on that.
- kodroid 4y agoThanks. Others have said similar, but more of a request for an additional post expanding on that aspect. Until then, there is a little more information in a previous related post https://doridori.github.io/Android-Architecture-Runtime/ https://doridori.github.io/Android-Architecture-Runtime/
- wizofaus 4y agoNitpick, but you have an unwanted apostrophe in your first sentence...
- kodroid 4y agoThanks, removed.
- monocularvision 4y agoLove this, thanks for sharing. This is fundamentally how our iOS application is modeled. We were highly inspired by Bernhardt’s talk and the Elm Architecture. It has lead to a very modular and maintainable app that has been very easy to test without ridiculous mocking you usually see when needing to interact with object-oriented / imperative frameworks like Cocoa Touch.
- kodroid 4y agoGreat, thanks for sharing also and your experience makes total sense. With this approach: 1. What downsides have you encountered? 2. How easy do you find it to onboard people? 3. Does Android do the same at your place?
- monocularvision 4y ago1. I can’t say I have found any real downsides yet. We have had to make improvements to support new capabilities, but it has been surprisingly easy to do so. Our most recent improvement was to formalize the concept of MVVM so that our ViewModels essentially acted in the same way as our Feature types. It allows us to continue using almost the same pattern at individual screen that we have been using at the feature level. Our next big effort will be fitting the architecture into SwiftUI, which I think will actually be also straightforward since SwiftUI is extremely functional. 2. Onboarding is probably the biggest “downside”. I would say there is absolutely a learning curve that requires some investment but everyone I speak to about it has been pretty positive after getting over that hurdle. And it feels like our productivity is very high. 3. Android does not follow the same pattern. I am planning on sharing your article with the team though. :-)
- kodroid 4y agoThanks. I love to see a description of that formalisation, if you ever get round to publishing it.
- monocularvision 4y ago