question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Compose functions to create objects

See original GitHub issue

I need to create an object that is composed of three function returns:

  • __find_stream_by_stream_id: returns stream property
  • __find_projects_by_stream: returns projects property
  • __get_stream_by_name: returns ksql_stream_detailed property

My problem here is to find a better way to compose all these calls to build my final Object because __find_projects_by_stream and __get_stream_by_name depends on the __find_stream_by_stream_id’s return. I’ve created __set_attr method to call it passing into .map to be able to set every object attribute without verifying with, if isinstance(result, Result.success_type), statement every time if I can extract the value of the result container a set it into my object.

Is there a better way to do this?

@dataclass
class StreamDetails:
    stream: Optional[Stream] = None
    projects: Optional[List[Project]] = None
    ksql_stream_detailed: Optional[KSQLStreamDetailed] = None


class GetStreamDetailsUsecase:
    def __call__(self, stream_id: UUID) -> Result[StreamDetails, FailureDetails]:
        stream_details = StreamDetails()
        return (
            self.__find_stream_by_stream_id(stream_id)
            .bind(self.__verify_if_stream_exist)
            .map(partial(self.__set_attr, stream_details, "stream"))
            .bind(self.__find_projects_by_stream)
            .map(partial(self.__set_attr, stream_details, "projects"))
            .bind(lambda _: self.__get_stream_by_name(stream_details.stream.name))  # type: ignore # noqa: E501
            .map(partial(self.__set_attr, stream_details, "ksql_stream_detailed"))
            .map(lambda _: stream_details)
        )

    def __set_attr(self, obj: Any, attr: str, value: Any) -> Any:
        setattr(obj, attr, value)
        return value

    def __verify_if_stream_exist(
        self, stream: Maybe[Stream]
    ) -> Result[Stream, BusinessFailureDetails]:
        if isinstance(stream, Maybe.success_type):
            return Success(stream.unwrap())
        return Failure(
            BusinessFailureDetails(
                reason="NOT_FOUND", failure_message="Stream not found"
            )
        )

Maybe the issue title is not so good, any suggestion will be welcome!

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:8 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
thepabloaguilarcommented, Jun 7, 2020

I think that I’ve reached a cool solution, so far! Thanks, @sobolevn for the support!

def get_stream(stream_id: uuid.UUID) -> ResultE[Dict[str, str]]:
    return Success({"stream_name": "my-stream", "stream_id": stream_id})


def get_projects(stream: str) -> ResultE[List[str]]:
    return Success([f"project-one-{stream}", f"project-two-{stream}"])


def get_ksql(stream_name: str) -> ResultE[Dict[str, Union[str, List[Any]]]]:
    return Success({"stream": stream_name, "fields": []})


if __name__ == '__main__':
    stream_id = uuid.UUID("ba16342a-4a8e-4951-8da0-73f61e168072")

    stream: ResultE[Dict[str, str]] = get_stream(stream_id)
    partial_projects: Callable[[], ResultE] = partial(stream.bind, get_projects)
    partial_ksql: Callable[[], ResultE] = partial(stream.bind, lambda s: get_ksql(s["stream_name"]))

    details = flow(
        Result.from_value(StreamDetails.build),
        stream.apply,
        bind(lambda to_apply: partial_projects().apply(Success(to_apply))),
        bind(lambda to_apply: partial_ksql().apply(Success(to_apply)))
    )

    print(details)
0reactions
thepabloaguilarcommented, Jun 7, 2020

My problem using flow in this situation is because of the dependency of one return value to two functions. Using flow will be something like this??

details = Result.from_value(StreamDetails.build)
flow(
    get_stream(stream_id),
    lambda stream: (stream.apply(details), stream),
    lambda input: (input[1].bind(get_projects).apply(input[0]), input[1]),
    lambda input: input[1].bind(lambda stream: get_ksql(stream["stream_name"])).apply(input[0])
)  # the output should be an instance of `StreamDetails`
# input[0] -> details
# input[1] -> stream

It’s just an example, don’t mind input[...] hahaha

@sobolevn do you see a better way?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Steps to Creating A Compose Function
We've composed a bunch of simple functions to build more complicated ones. Using functional composition we can break our code into smaller ...
Read more >
Thinking in Compose | Jetpack Compose
Using Compose, you can build your user interface by defining a set of composable functions that take in data and emit UI elements....
Read more >
From object to function composition - Hemant Kumar
Composition is a technique often used to build software by creating cohesive units that interact with each other to form the overall system....
Read more >
How to use powerful function composition in Javascript
Function composition is one of the most powerful differentiators between object-oriented and functional approaches to programming in ...
Read more >
Composition
With composition, we compose the functionality of more complex objects. Instead of focusing on what an object is, we focus on what an...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found