DBUS_TYPE_SUM ? DBUS_TYPE_UNIT ? DBUS_TYPE_DEPENDENT_PAIR ?

Simon McVittie simon.mcvittie at collabora.co.uk
Mon Dec 16 03:17:21 PST 2013


On 14/12/13 11:38, Andrew Cann wrote:
> On the subject of new types that could be added to DBus, it would be cool if 
> there was a type for representing sum types in languages with algebraic
> datatypes.

As far as "non-magical" types go, applying conventions on top of the
current type system can encode whatever you want. The relevant question
for extensions to the type system is: is the cost of managing forward-
and backward-compatibility, enhancing every implementation and binding,
etc., less than the value of the new type? In my opinion, the answer is
nearly always "no".

(UNIX_FD is the only current example of a "magical" type - it has
semantics that couldn't be done in terms of a byte array containing
serialized memory. Unlike "maybe", tagged unions, etc., it's a new
feature that couldn't be done without extending the wire protocol.)

GVariant has two new types not present in D-Bus: () (empty tuple,
"unit"), and mX ("maybe X") where X is any type.

() is pretty useless, but is an obvious extension of the type system to
allow something that, with hindsight, probably shouldn't have been
forbidden in the first place.

"maybe X" can be emulated with "array of X" and some documentation that
says there will be at most one X in the array (and in fact the proposed
implementation of "maybe" has an identical wire-protocol encoding, I think).

> For instance, the DBus equivalent of the haskell type
> 
> data MyType = Foo Int32
>             | Bar Int32
>             | Baz String
> 
> would have the signature "[iis]", meaning a value of this type would be either
> the first int32, the second int32, or a string.

If forced to implement exactly this, I'd encode it as (iv), where the i
is an enum { Foo, Bar, Baz } and the v is documented to contain i, i or
s for Foo, Bar or Baz respectively.

However, what I'd *actually* do would be to send an a{sv} and document
some possible keys and values, and as a result, have a way to pass more
information in future without breaking the D-Bus API. (See the Telepathy
specification, which uses that type all over the place.)

> A sum type
> could be used as a generalisation of this so "mi" would instead be "[()i]", ie.
> either the empty struct or an int32.

As noted above, () can't actually exist in current D-Bus (although
GVariant includes it as an extension).

> Better yet, introduce a unit/void type
> with the signature ".", then a Maybe Int32 could be signed "[.i]".

GVariant calls () G_VARIANT_TYPE_UNIT, so I'd rather not have a separate
unit type.

> Another, perhaps crazier, but potentially useful type is the dependent pair
> type where a value can depend on a type passed at runtime. This could be useful
> for sending generic types over dbus.

If you're sending generic types over D-Bus, and you want any possibility
of interoperability, then you need some good documentation. If you have
reasonable documentation, you might as well use a{sv} or other
variant-based types.

> I've been in a situation before where I wanted to pass an array of variants
> down the wire but I wanted all the array elements to be of the same, unknown,
> type. You could handle sitations like this by having a type that consists of a
> signature for a type T, and a value of a type defined in terms of T.

Three encodings I can immediately think of for this:

* av (array of variant) with documentation that says their types are
  all the same; example value (in GVariant notation)
  @av [<int32 1>, <int32 2>, <int32 3>]
  (this is the most obvious but least efficient)

* a single variant with documentation that says it contains an array of
  something; example value <@ai [int32 1, int32 2, int32 3]>

* a (gv) (pair of (signature, variant)) with documentation that says
  the variant contains an array of the type given by the signature;
  example value (signature 'i', <@ai [int32 1, int32 2, int32 3]>)
  (this is only worthwhile if you need to be able to distinguish
  between empty arrays of distinct types, which, in practice, you won't)

I'm not interested in breaking wire-protocol (and potentially ABI)
compatibility to enable this sort of thing to be type-checked. In
practice, real-world D-Bus APIs either use relatively simple types, or
a{sv}, and have lots of type/value constraints that are documented but
not mechanically checked. The D-Bus wire protocol is not suitable for
Haskell-like type distinctions, unless you encode them yourself, which
you can already do with the current types: we're never going to offer
user-defined string subtypes, for instance.

> Using this, you could specify very complicated types.

But I don't want to do that. :-P

I'm only partially joking there: I have no interest in making the D-Bus
type system arbitrarily complicated. The vast majority of D-Bus APIs use
a small subset as it is, and I'd rather spend time educating D-Bus users
on how to make best use of the current type system and object model
(many don't), or on fixing bugs, than on extending the type system or
object model.

> By the way are there actually any real plans to extend
> DBus with major new features like this? Or at this point is the specification
> pretty much set in concrete?

Not exactly concrete, but perhaps a brick wall or something. :-)

The more the type system changes, the more we break interoperability
between existing implementations and bindings. Interoperability between
existing implementations and bindings is a large part of what D-Bus is,
so anything that can break that needs a really good justification.

    S



More information about the dbus mailing list