Hi. I have a question/proposal about dry-monads.
I wrote code like this:
module Website
class HandleNewOrder
include Dry::Monads::Either::Mixin
include AppContainer::Inject[
'bus.find_or_create_client',
'bus.create_order'
]
def call(order_params)
@find_or_create_client.call(extract_client_params(order_params))
.fmap { |client| build_bus_order_params(order_params, client) }
.bind(@create_order)
end
...
end
find_or_create_client -returns Right monad with client object inside,
and then i want to ajdust parameters for @create_order without breaking chain (.fmap)
I’m wondering, is it posible to send additional params to the next .bind (which will reduce fmap call from chain ).
Something like this:
def call(order_params)
@find_or_create_client.call(extract_client_params(order_params))
.bind(@create_order, order_params) # Should call: create_order.call(client, order_params)
end
Or it’s not idiomatic for monads? (I’m not familiar with theory of this concept)
Hello.
That’s a good question. Normally, you’d use currying for this, but Ruby’s support for it is rather cumbersome, have a look:
def call(order_params)
create_with_params = @create_order.method(:call).curry.(order_params) # you have to change the order of .call arguments to make it work
@find_or_create_client.call(extract_client_params(order_params))
.bind(create_with_params)
end
Adding currying arguments as extra args to .bind
/.fmap
is a nice idea fwiw. I personally use keywords for this and it looks nice to me
def find_or_create_client(param1: param2: , **rest)
client = # do some stuff using param1 and param2
Right(
client: client,
**rest # just pass anything else "as is"
)
end
def create_order(order_params: , client: **rest)
order = # do some stuff with order_params and client
Right(
client: client,
order: order,
**rest # we probably won't need order_params anymore, so we don't pass it here, but we can if needed
)
end
def call(order_params)
find_or_create_client(order_params: order_params).fmap(method(:create_order))
end
I found using keywords more flexible, you can chain you operations more easily with them. But anyway, as I said passing extra arguments to .bind
is a good idea and should make no harm (except it will became a tiny bit slower) so I’m going to give it a shot.