Composing typed behaviors

38 views
Skip to first unread message

Alexander Gagarin

unread,
Feb 28, 2020, 2:05:42 PM2/28/20
to actor-framework
Hi!

I wrote the followig function:

template<typename... SigsA, typename... SigsB>
auto first_then_second(caf::typed_behavior<SigsA...> first, caf::typed_behavior<SigsB...> second) {
   
using namespace caf::detail;

   
using SigsAB = type_list<SigsA..., SigsB...>;
   
using result_t = tl_apply_t<tl_distinct_t<SigsAB>, caf::typed_behavior>;

   
return result_t{
       
typename result_t::unsafe_init(),
        caf
::message_handler{ first.unbox().as_behavior_impl() }
       
.or_else( second.unbox() )
   
};
}

It basically models `or_else()` for typed behaviors. My actors that uses `first_and_second()` seems to be working fine at fist glance.
The main questing is why `or_else()` is missing in `typed_behavior`?

I read that composition is provided for `message_handler` and behavior contains 'optional timeout'. But if for every request we must pass an explicit timeout, then what this extra timeout inside behavior is used for? BTW I failed to find any example in documentation that demostrate how to set and/or use this hidden timeout.

After all what are the drawbacks of function above? Can I rely on such implementation?
I know that there exists a composable behavior API, but it's a little bit awkward in my opinion (compared to `message_handler` and `behavior`) and I just don't want to use it. At the same time I want to manually turn on compile-time checks for interface in nested actor that extends base actor. Thats why I finally end up with solution above.

Dominik Charousset

unread,
Mar 3, 2020, 2:30:26 PM3/3/20
to actor-f...@googlegroups.com
> template<typename... SigsA, typename... SigsB>
> auto first_then_second(caf::typed_behavior<SigsA...> first, caf::typed_behavior<SigsB...> second) {
> using namespace caf::detail;
>
> using SigsAB = type_list<SigsA..., SigsB...>;
> using result_t = tl_apply_t<tl_distinct_t<SigsAB>, caf::typed_behavior>;
>
> return result_t{
> typename result_t::unsafe_init(),
> caf::message_handler{ first.unbox().as_behavior_impl() }
> .or_else( second.unbox() )
> };
> }
>
> It basically models `or_else()` for typed behaviors. My actors that uses `first_and_second()` seems to be working fine at fist glance.
> The main questing is why `or_else()` is missing in `typed_behavior`?
>
> I read that composition is provided for `message_handler` and behavior contains 'optional timeout'. But if for every request we must pass an explicit timeout, then what this extra timeout inside behavior is used for? BTW I failed to find any example in documentation that demostrate how to set and/or use this hidden timeout.

Hm, after grepping through the manual and the examples... Timeouts really aren’t covered anywhere. That’s a rather silly omission.

FWIW, this is how you can set timeouts in CAF:


behavior foo(event_based_actor* self) {
return {
[=](const std::string& str) { bar(str); },
after(std::chrono::seconds(1)) >>
[=] {
// ... received nothing for 1s ...
},
};
}

The difference between message_handler and behavior is exactly that the former won’t allow you to assign an after(...) timeout to it.

> After all what are the drawbacks of function above? Can I rely on such implementation?
> I know that there exists a composable behavior API, but it's a little bit awkward in my opinion (compared to `message_handler` and `behavior`) and I just don't want to use it. At the same time I want to manually turn on compile-time checks for interface in nested actor that extends base actor. Thats why I finally end up with solution above.

Yeah, we aren’t really happy with the composable behavior API either. That’s why it never left the experimental state (and we consider replacing it).

I think your code is fine for now if you’re not using timeouts. It is using private API (as_behavior_impl), but I don’t see a way around that at the moment as long as CAF doesn’t implement a typed version of message_handler. That’s something we need to fix in the API.

Dominik

Alexander Gagarin

unread,
Mar 10, 2020, 1:54:04 PM3/10/20
to actor-framework
Thanks for explanation, Dominik!

I think your code is fine for now if you’re not using timeouts. It is using private API (as_behavior_impl), but I don’t see a way around that at the moment as long as CAF doesn’t implement a typed version of message_handler. That’s something we need to fix in the API.

If my imlementation is fine, then timeouts will be removed from behavior, if any, because I convert both to `message_handle`, right? I don't use timeouts at this time, but just curious.

Dominik Charousset

unread,
Mar 13, 2020, 1:09:11 PM3/13/20
to actor-f...@googlegroups.com
>> I think your code is fine for now if you’re not using timeouts. It is using private API (as_behavior_impl), but I don’t see a way around that at the moment as long as CAF doesn’t implement a typed version of message_handler. That’s something we need to fix in the API.
>
> If my imlementation is fine, then timeouts will be removed from behavior, if any, because I convert both to `message_handle`, right? I don't use timeouts at this time, but just curious.

If you compose two behaviors in this way, you’ll end up using the timeout of the second behavior. So, `a.or_else(b)` will use whatever timeout is defined in `b`.

Dominik
Reply all
Reply to author
Forward
0 new messages