[Solved] API harmonisation #23
Replies: 2 comments 3 replies
-
DeclarationA zenoh session allows to perform a set of declarations to interact with the overall zenoh system. Declarations are by nature revertible, i.e. everything that is being declared can be undeclared at a later stage. In the same way as declarations are done on the session, undeclarations are done on the same session. RustBelow you can find a common approach to declarations in Rust. Below you can find an example based on the current 0.5.0-beta.9 implementation and types. trait ZDeclaration {
fn undeclare_inner(self, session: &Session) -> ZResult<()>;
}
impl<'a> ZDeclaration for KeyExpr<'a> {
fn undeclare_inner(self, session: &Session) -> ZResult<()> {
session.undeclare_expr(self.as_id()).wait()
}
}
impl<'a> ZDeclaration for Subscriber<'a> {
fn undeclare_inner(mut self, session: &Session) -> ZResult<()> {
self.alive = false;
session.unsubscribe(self.state.id).wait()
}
}
impl<'a> ZDeclaration for Queryable<'a> {
fn undeclare_inner(mut self, session: &Session) -> ZResult<()> {
self.alive = false;
session.close_queryable(self.state.id).wait()
}
}
impl Session {
pub fn undeclare<'a, D>(&'a self, d: D) -> ZResult<()>
where
D: ZDeclaration,
{
d.undeclare_inner(&self)
}
} By doing so, any session.undeclare(subscriber).unwrap();
session.undeclare(publisher).unwrap();
session.undeclare(queryable).unwrap();
session.undeclare(querier).unwrap();
session.undeclare(keyexpr).unwrap(); Additionally, for those types that carry internally a reference to the session, the subscriber.undeclare().unwrap();
publisher.undeclare().unwrap();
queryable.undeclare().unwrap();
querier.undeclare().unwrap(); SubscriberA subscriber is a declaration. Therefore, the function should refer to the RustIn Rust, the builder pattern is resolved by the function pub fn declare_subscriber<'a, 'b, IntoKeyExpr>(
&'a self,
key_expr: IntoKeyExpr,
) -> SubscriberBuilder<'a, 'b>
where
IntoKeyExpr: Into<KeyExpr<'b>>;
// --- SYNC
use zenoh::sync::*;
// Callback
let s = session.declare_subscriber("/a").callback(|s| {}).res().unwrap();
// s does not implement .next()
s.undeclare().unwrap(); // shorthand for session.undeclare(s).unwrap();
// Stream
let s = session.declare_subscriber("/a").channel(flume::bounded(64)).res().unwrap();
// s implements .next()
s.undeclare().unwrap(); // shorthand for session.undeclare(s).unwrap();
// Default shorthand for session.subscribe("/a").channel(flume::bounded(64))
let s = session.declare_subscriber("/a").res().unwrap();
s.undeclare().unwrap(); // shorthand for session.undeclare(s).unwrap();
// --- ASYNC
use zenoh::async::*;
// Callback
let s = session.declare_subscriber("/a").callback(|s| async {}).res().await.unwrap();
s.undeclare().await.unwrap(); // shorthand for session.undeclare(s).await.unwrap();
// Stream
let s = session.declare_subscriber("/a").channel(flume::bounded(64)).res().await.unwrap();
s.undeclare().await.unwrap(); // shorthand for session.undeclare(s).await.unwrap();
// Default shorthand for session.subscribe("/a").channel(flume::bounded(64))
let s = session.declare_subscriber("/a").res().await.unwrap();
s.undeclare().await.unwrap(); // shorthand for session.undeclare(s).await.unwrap(); CIn C, an iterator abstraction will be built departing from the callback-based API. z_owned_subscriber_t sub = z_declare_subscriber(z_loan(s),
z_expr("/a"), data_handler, NULL, z_sopt_default());
z_undeclare_subscriber(z_move(sub)); Python# --- SYNC
import zenoh.sync as zenoh
sub = session.declare_subscriber(key)
sub.undeclare() # shorthand for session.undeclare(sub)
# --- ASYNC
import zenoh.async as zenoh
sub = await session.declare_subscriber(key)
await sub.undeclare() # shorthand for await session.undeclare(sub) PublisherA publisher is a declaration. Therefore, the function should refer to the Rustpub fn declare_publisher<'a, 'b, IntoKeyExpr>(
&'a self,
key_expr: IntoKeyExpr,
) -> PublisherBuilder<'a, 'b>
where
IntoKeyExpr: Into<KeyExpr<'b>>;
// --- SYNC
use zenoh::sync::*;
// Default
let p = session.declare_publisher("/a").res().unwrap();
loop {
p.put(data).unwrap();
}
// s does not implement .next()
p.undeclare().unwrap(); // shorthand for session.undeclare(p).unwrap();
// --- ASYNC
use zenoh::async::*;
// Default
let p = session.declare_publisher("/a").res().await.unwrap();
loop {
p.put(data).await.unwrap();
}
// s does not implement .next()
p.undeclare().await.unwrap(); // shorthand for session.undeclare(p).unwrap(); Cz_owned_publisher_t pub = z_declare_publisher(z_loan(s),
z_expr("/a"), z_popt_default());
z_put_pub(z_loan(pub), data);
z_undeclare_publisher(z_move(sub)); Python# --- SYNC
import zenoh.sync as zenoh
pub = session.declare_publisher(key)
pub.put(data)
pub.undeclare() # shorthand for session.undeclare(sub)
# --- ASYNC
import zenoh.async as zenoh
pub = await session.declare_publisher(key)
await pub.put(data)
await pub.undeclare() # shorthand for await session.undeclare(sub) QueryableA queryable is a declaration. Therefore, the function should refer to the Rustpub fn declare_queryable<'a, 'b, IntoKeyExpr>(
&'a self,
key_expr: IntoKeyExpr,
) -> QueryableBuilder<'a, 'b>
where
IntoKeyExpr: Into<KeyExpr<'b>>;
// --- SYNC
use zenoh::sync::*;
// Callback
let q = session.declare_queryable("/a").callback(|s| {}).res().unwrap();
// q does not implement .next()
q.undeclare().unwrap(); // shorthand for session.undeclare(q).unwrap();
// Stream
let q = session.declare_queryable("/a").channel(flume::bounded(64)).res().unwrap();
// q implements .next()
q.undeclare().unwrap(); // shorthand for session.undeclare(q).unwrap();
// Default shorthand for session.subscribe("/a").channel(flume::bounded(64))
let q = session.declare_queryable("/a").res().unwrap();
q.undeclare().unwrap(); // shorthand for session.undeclare(q).unwrap();
// --- ASYNC
use zenoh::async::*;
// Callback
let q = session.declare_queryable("/a").callback(|s| async {}).res().await.unwrap();
q.undeclare().await.unwrap(); // shorthand for session.undeclare(q).await.unwrap();
// Stream
let q = session.declare_queryable("/a").channel(flume::bounded(64)).res().await.unwrap();
q.undeclare().await.unwrap(); // shorthand for session.undeclare(q).await.unwrap();
// Default shorthand for session.subscribe("/a").channel(flume::bounded(64))
let q = session.declare_queryable("/a").res().await.unwrap();
q.undeclare().await.unwrap(); // shorthand for session.undeclare(q).await.unwrap(); CIn C, an iterator abstraction will be built departing from the callback-based API. z_owned_queryable_t qbl = z_declare_queryable(z_loan(s),
z_keyexpr("/a"), query_handler, NULL, z_qopt_default());
z_undeclare_queryable(z_move(qbl)); Python# --- SYNC
import zenoh.sync as zenoh
qbl = session.declare_queryable(key)
qbl.undeclare() # shorthand for session.undeclare(qbl)
# --- ASYNC
import zenoh.async as zenoh
qbl = await session.declare_queryable(key)
await qbl.undeclare() # shorthand for await session.undeclare(qbl) QuerierA querier is a declaration. Therefore, the function should refer to the Rustpub fn declare_querier<'a, 'b, IntoKeyExpr>(
&'a self,
key_expr: IntoKeyExpr,
) -> QuerierBuilder<'a, 'b>
where
IntoKeyExpr: Into<KeyExpr<'b>>;
// --- SYNC
use zenoh::sync::*;
// Default
let q = session.declare_querier("/a").res().unwrap();
loop {
while let Some(r) = q.get() {
match r {
Ok(r) => {},
Err(e) => {},
}
}
}
// s does not implement .next()
q.undeclare().unwrap(); // shorthand for session.undeclare(q).unwrap();
// --- ASYNC
use zenoh::async::*;
// Default
let q = session.declare_querier("/a").res().await.unwrap();
loop {
while let Some(r) = q.get().await {
match r {
Ok(r) => {},
Err(e) => {},
}
}
}
// s does not implement .next()
q.undeclare().await.unwrap(); // shorthand for session.undeclare(q).unwrap(); Cz_owned_querier_t pub = z_declare_querier(z_loan(s),
z_keyexpr("/a"), z_qopt_default());
z_get_query(z_loan(pub), data);
z_undeclare_publisher(z_move(sub)); Python# --- SYNC
import zenoh.sync as zenoh
pub = session.declare_publisher(key)
pub.put(data)
pub.undeclare() # shorthand for session.undeclare(sub)
# --- ASYNC
import zenoh.async as zenoh
pub = await session.declare_publisher(key)
await pub.put(data)
await pub.undeclare() # shorthand for await session.undeclare(sub) KeyExprAn expression is a declaration. Therefore, the function should refer to the RustDespite the ongoing discussion mentioned above, for what concerns the Rust API, it should be aligned with the other API and potentially make the pub fn keyexpr<'a, IntoKeyExpr>(&'a self, key_expr: IntoKeyExpr) -> KeyExprBuilder<'a>
where
IntoKeyExpr: Into<KeyExpr<'a>>;
// --- SYNC
use zenoh::sync::*;
let k = session.declare_keyexpr("/a").res().unwrap();
session.undeclare(k).unwrap();
// --- ASYNC
use zenoh::async::*;
let k = session.declare_keyexpr("/a").res().await.unwrap();
session.undeclare(k).unwrap(); Cz_keyexpr_t k = z_declare_keyexpr(s, z_keyexpr("/a"), z_eopt_default());
int r = z_undeclare_keyexpr(s, k); Python# --- SYNC
import zenoh.sync as zenoh
k = session.declare_keyexpr("/a")
session.undeclare(k)
# --- ASYNC
import zenoh.async as zenoh
k = await session.declare_keyexpr("/a")
await session.undeclare(k) NOTE: additional information will be posted shortly |
Beta Was this translation helpful? Give feedback.
-
ActionsBelow you can find a common approach to actions in Rust. Below you can find an example based on the current implementation and types. PutA put is an action. Therefore, the function should refer to the Rustpub fn put<'a, IntoKeyExpr, IntoValue>(
&'a self,
key_expr: IntoKeyExpr,
value: IntoValue,
) -> PutBuilder<'a>
where
IntoKeyExpr: Into<KeyExpr<'a>>,
IntoValue: Into<Value>;
// --- SYNC
use zenoh::sync::*;
// Default parameters
session.put("/a", data).res().unwrap();
// Optional parameters
session
.put("/a", data)
.congestion_control(CongestionControl::Block)
.res()
.unwrap();
// --- ASYNC
use zenoh::async::*;
// Default parameters
session.put("/a", data).res().await.unwrap();
// Optional parameters
session
.put("/a", data)
.congestion_control(CongestionControl::Block)
.res()
.await
.unwrap(); Cint r = z_put(s, keyexpr, (const uint8_t *)data, len, z_popt_default()); Python# --- SYNC
import zenoh.sync as zenoh
session.put(key, value)
# --- ASYNC
import zenoh.async as zenoh
await session.put(key, value) GetA get is an action. Therefore, the function should refer to the Rustpub fn get<'a, 'b, IntoSelector>(&'a self, selector: IntoSelector) -> GetBuilder<'a, 'b>
where
IntoSelector: Into<Selector<'b>>;
// --- SYNC
use zenoh::sync::*;
// Default: stream;
let r = session.get("/a").res().unwrap();
// Stream
let r = session
.get("/a")
.channel(flume::bounded(64))
.res()
.unwrap();
// Callback
session
.get("/a")
.callback(|s| {})
.res()
.unwrap();
// --- ASYNC
use zenoh::async::*;
// Default: stream;
let r = session.get("/a").res().await.unwrap();
// Stream
let r = session
.get("/a")
.channel(flume::bounded(64))
.res()
.await
.unwrap();
// Callback
session
.get("/a")
.callback(|s| {})
.res()
.await
.unwrap(); CIn C, an iterator abstraction will be built departing from the callback-based API. int r = z_get(s, z_keyexpr("/a"), reply_handler, NULL, z_gopt_default()); Python# --- SYNC
import zenoh.sync as zenoh
# Callback
session.get("/a", callback=reply_handler)
# Stream
let replies = session.get("/a")
# --- ASYNC
import zenoh.async as zenoh
# Callback
await session.get("/a", callback=reply_handler)
# Stream
let replies = await session.get("/a") |
Beta Was this translation helpful? Give feedback.
-
The following covers some aspects about rationalisation of APIs, their semantics, and implied behaviour for Rust, C, and Python.
Philosophy
Properly naming the functions has been a great ground for debate. In the following I propose a common philosophy for function naming and the implied behaviour.
Concept
Let’s start by categorising the zenoh session functions in two sets:
Declarations
Functions that perform a certain declaration is expected to have a long-lived impact on the zenoh system to a certain degree. E.g. some permanent state is maintained in the network, information is distributed, some mechanism is enabled in the system.
That means, when performing a declaration, the zenoh system (either next hop or far away) will potentially react and adapt its behaviour according to the declaration. As such, the amount of declarations in a given system needs to be maintained into reasonable limits. Moreover, declarations represent something that can be reverted. I.e., if something can be declared then it can also be undeclared. Finally, nouns should be used to name declaration functions.
Example of declarations are:
Actions
Functions that perform a certain action is expected to have a none or short-living impact on the zenoh system to a certain degree. That means, when performing an action the zenoh system is expected to simply deliver or retrieve data. As such, a given zenoh system should be able to support a large number of concurrent actions. In other words, we could refer to actions as the set of functions that belong to the critical path. Moreover, actions represent some action that can be reverted per-se. I.e., a
put
on the network can not be reverted but a second action likedelete
can be provided to undo the effects of a put at a certain degree. Finally, verbs should be used to name action functions.Example of actions are:
NOTE: in the current API version (0.5.0-beta.9) the declare_ is intended to mark optional zenoh functions. In the version proposed below, the term/function declare is instead used for marking a declaration.
Future extensibility
It is desirable to keep in the future the possibility to extend the current API with additional parameters and functionalities. However, this shouldn’t have an impact on the function signature and it should be rather handled in a future-proof manner.
Depending on the programming language, future extensibility can be provided by three paradigms:
Sync and Async
It has been debated wether
sync
andasync
API should be provided separately or by the same type.The main goal is to avoid the uncareful user to mix
sync
andasync
calls if not expressly desired.More discussion can be found here: ZFuture.
The proposal below it hence adopts the
declaration
andaction
separation together with thesync
andasync
separated APIs.NOTE: additional information will be posted shortly
Beta Was this translation helpful? Give feedback.
All reactions