Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Expressing Business Flows Using an F# DSL (medium.com/jettech)
95 points by jnovino on March 22, 2019 | hide | past | favorite | 6 comments


Thanks for the write-up, very interesting! Reading it I had a few questions:

1. How can DAG based workflows express events that are iterative in nature, ie. would result in a cycle? For example I have an empty shopping cart as a start state and I want to put X items in it?

2. Is undo/rollback possible? If so how does cleanup work and does an undo/rollback means forking into a new state as opposed to going back to a previous state we logically want to be in?


cart { let! item1 = item let! item2 = anotherItem … }

No cycle there! You'd use some kind of state monad to keep track, and each let! simply adds something to the state. For example, you could end up with the computation expression producing a `Cart = Cart of Item list`, which contains a list of the things that were added.

Your question about going back to a previous state: it depends on what style you're using.

* At my day job, we use the "initial algebra" style (which is something of an abuse of F#'s type system but which gives you absolute power). In this style, your computation expression produces a structure isomorphic to a list of "this was added, then this was added, then this was removed, then this was added, …". It is then the job of some further processing step to convert this into more useful forms, like deduplicating carts which have the same contents.

* One could instead produce the cart as you went, and it would Just Work (tm). Note that in F#, most data structures are immutable, so "rolling back" to the list [item1; item2] from the list [item3; item1; item2] is exactly the same as taking the tail of that input list. You won't fork the state by producing another list which happens to have the same contents; they are the same list. In general, F# structures tend to do structural equality.


Sorry, I probably wasn't clear in my initial question. What I meant is how do DAG based workflows deal with computational graphs that contains cycles? Eg. we have 2 states: Cart and Payment and 2 events: PutItem and Checkout. A simple (external) DSL describing this graph could look like:

  Cart --PutItem--> Cart
  Cart --Checkout-> Payment
Does the workflow engine in the article exclude situations like this?

Regarding forking what I meant is that if you have:

  A --event--> B
  ^           /
   \--undo---/
then if your invariant is to be a DAG then you could "unroll" the cycle into some new state C that you would make semantically equivalent to A.

  A --event--> B --undo--> C
This works but causes state explosion.


This is an excellent article.

The thing I noted that bears true with my experience: it's an evolution. As you come to understand the business better and better, you naturally evolve through some of the same steps.

To me, the steps feel something like imperative->repl->functional->pure functional->microservices->DSLs

I think if you keep to your code budgets at each step of this, it works out quite nicely. F# is a great tool for this kind of evolution because it's both multi-paradigm and plugs into a ton of other stuff.

Thanks guys!

ADD: It would be a mistake to read articles like this and jump into a DSL, in my opinion. I know a lot of guys who read some really cool books out there on things like DDD and want to start there. What's missing in articles and books like this is the backstory. These folks ended up where they were and were able to do what they're doing now because of a ton of learning. Not learning code. The business, the tech, and the customers all learning from one another. I don't see any way that this doesn't take a good bit of time.


F# language has a concept called "computation expressions" which are related to monads. They are also sometimes called workflows. But the article is using "workflow" in the more common language way which was confusing to me at first.


IIRC they are essentially Monads, but someone decided that was too scary a name. The result is confusion for experienced programmers, ironically the ones more likely to use F# (!)




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

Search: