I’ve been reading about the OO ‘fluent interface’ approach in Java, JavaScript and Scala and I like the look of it, but have been struggling to see how to reconcile it with a more type-based/functional approach in Scala.
To give a very specific example of what I mean: I’ve written an API client which can be invoked like this:
val response = MyTargetApi.get("orders", 24)
The return value from get() is a Tuple3 type called RestfulResponse, as defined in my package object:
// 1. Return code
// 2. Response headers
// 2. Response body (Option)
type RestfulResponse = (Int, List[String], Option[String])
This works fine – and I don’t really want to sacrifice the functional simplicity of a tuple return value – but I would like to extend the library with various ‘fluent’ method calls, perhaps something like this:
val response = MyTargetApi.get("customers", 55).throwIfError()
// Or perhaps:
MyTargetApi.get("orders", 24).debugPrint(verbose=true)
How can I combine the functional simplicity of get() returning a typed tuple (or similar) with the ability to add more ‘fluent’ capabilities to my API?
It seems you are dealing with a client side API of a rest style communication. Your
getmethod seems to be what triggers the actual request/response cycle. It looks like you’d have to deal with this:I think for the properties of the transport, you can put some of it into the constructor of the
MyTargetApiobject, but you can also create a query object that will store those for a single query and can be set in a fluent way using aquery()method:This would return some stateful
Queryobject that stores the value for log level, error handling. For providing the data for the input, you can also use the query object to set those values but instead of returning your response return aQueryResult:Then to eventually get the results you call
run. So at the end of the day you can call it like this:Which should be a verbose request that will error out on issue, retrieve the customers with id 22, keep the body and get its length as an
Option[Int].The idea is that you can use
mapto define computations on a result you do not yet have. If we addflatMapto it, then you could also combine two computations from two different queries.