swlaschin / RailwayOrientedProgramming

Railway Oriented Programming slides and code
92 stars 11 forks source link

Railway Oriented Programming in interpreted languages like Ruby #4

Open blatter2016 opened 4 months ago

blatter2016 commented 4 months ago

Hi Scott,

This is more of a bunch of questions than an issue.

For some background, my team and I are big fans of your work and we’re kind of in the middle of adopting Railway Oriented Programming in our little corner of the world. Specifically, in our codebase, we’ve taken inspiration from the Result datatype and we have crafted most of our functions/methods to return it, so that they can be plumbed together as clear, readable pipelines.

An example would be something like:

Result.new(input_params)
  .and_then(:&validateOrder)
  .and_then(:&commitOrder)
  # … and so on

However, we are primarily a Ruby shop, and porting ROP to work in Rubyland has led to quite a few discussions, especially regarding what must be passed along in these pipelines. To use the snippet shared above, what should the inputs of validateOrder andcommitOrder`?

Since Ruby lacks any compile time checking, we’ve tried to work around this by enforcing some styling standards:

To ground it with example using the shared snippet above, the following would be the definitions of validateOrder and commitOrder functions:


def validateOrder(hash):
  user_id = hash[“user_id”]
  product_id = hash[“product_id”]
  ….
  hash[“validated_order”] = ValidatedOrder(user_id, product_id) 

def commitOrder(hash):
  validated_order = hash[“validated_order”]
  …

While this seems to work for us for the time being, there are concerns about how such a methodology scales as the business logic grows more complex, when more pipeline stages must be added and more data requires passing from one stage to another.

While the high-level pipeline looks very elegant and concisely describes the business logic, a concern is how this intermediate hash object grows in complexity/size.

So I guess my questions are the following:

Request |> createUser |> generateVerificationEmail |> doSomethingWithUser // how does user from createUser find its way here


- I understand that your codebase for ROP is mostly in F# (which is fantastic btw), but what are your thoughts/opinions on the portability to other languages (say Ruby for ex). There are tons of libraries out there for Ruby that claim inspiration from ROP but seem to do so through DSLs that differ significantly/create a learning curve which I’m not sure is worth it.

Apologies for the long essay, but it would be great to hear your thoughts. Do let me know if I’ve been unclear anywhere or if I can elaborate on any detail.

Regards