Host language independent system

16 views
Skip to first unread message

Magnus Holm

unread,
Mar 19, 2014, 5:00:19 PM3/19/14
to q-dat...@googlegroups.com
Hi,

I've been playing a bit with Q and I have a few thoughts.

Here's the context: I'm building a REST API and want to use Q to
specify all of the data types. The goal is that the system files
serves two purposes: *Validating* the input and *documenting* the data
types.

The documentation is the most important aspect here, so I want the
system files to be readable (and usable) to as wide audience as
possible. This means that anything that's Ruby-specific has to go.

However, I find the support for host-independent system a bit …
lacking. Some examples:

## ADTs depends on a constant name (so it's not actually host-independent)

ADT seems to be the intended feature when you want to use custom Ruby
classes for your data structure. However, whenever I try to use them
as such, I end up with code like this:
```
Article = .MyApp::Models::Article <article> {

}
```

* This requires the full constant name
* I'll have to write "article" three times

## Why can't I just hook up a tuple to a Ruby class?

An "article" in this system is just a dumb tuple; there's not multiple
possible representations (as with colors) of an article. Yet, I still
want dressing to produce instances of my Article-class.

I want to do something like this:

```
system["Article"].ruby_class = MyApp::Models::Article
```

## What exactly is the purpose of ADT?

I think ADTs (and the whole "information contract" thing) sounds
really interesting, but I'm failing to see the value. Why not just do
it like this:

```
ColorTuple = {r: Byte, g: Byte, b: Byte}
ColorHex = String

Color = ColorTuple|ColorHex

Article = {
color: Color # both are supported
background_color: ColorTuple # because of legacy reasons, you can
only pass in a tuple here
}
```

Combine this with a Ruby-level API for dressing any Q type into a Ruby
class and it gives you greater flexibility than ADTs do today.

---

Cheers,
Magnus

Bernard Lambeau

unread,
Mar 20, 2014, 5:12:55 AM3/20/14
to Magnus Holm, q-dat...@googlegroups.com
Hi Magnus,

Thanks for sharing your concerns and thoughts. I reply inline below.

Here's the context: I'm building a REST API and want to use Q to
specify all of the data types. The goal is that the system files
serves two purposes: *Validating* the input and *documenting* the data
types.

The documentation is the most important aspect here, so I want the
system files to be readable (and usable) to as wide audience as
possible. This means that anything that's Ruby-specific has to go.

However, I find the support for host-independent system a bit …
lacking.

I share your concern about host-(in)dependence. Some work has to be done in Q/Finitio to achieve this goal. We already discussed about defining a constraint language that abstracts from host, and I didn't change my mind since then. You also share very good examples/concerns below, that however require further analysis.

(That said, I don't plan to actually go as far as removing any dependence to host, because I don't think it is wise to do so. I'm more looking from having smooth ways of building host-independent systems on top of host-dependent systems. I think that the way we recently built the acceptance test suite shared between Qrb and Qjs is a good example of that. See https://github.com/blambeau/q-lang/tree/master/features)
 

## ADTs depends on a constant name (so it's not actually host-independent)

ADT seems to be the intended feature when you want to use custom Ruby
classes for your data structure. However, whenever I try to use them
as such, I end up with code like this:
```
Article = .MyApp::Models::Article <article> {
  …
}
```

* This requires the full constant name
* I'll have to write "article" three times

Indeed. Now, if you're not interested in actually dressing information but only validating/documenting data/APIs, you can actually use this:

```
Article = <as> {
   ...
}
```

The "official" syntax (sorry, not very well documented so far) allows this. If a binding does not allow it, it's a bug. More about '<as>' below.
 

## Why can't I just hook up a tuple to a Ruby class?

An "article" in this system is just a dumb tuple; there's not multiple
possible representations (as with colors) of an article. Yet, I still
want dressing to produce instances of my Article-class.

I want to do something like this:

```
system["Article"].ruby_class = MyApp::Models::Article
```

So you don't only want to validate/document, but also dress from low-level information à la JSON to higher-level abstractions.

For now, Finitio (I learn to use that name from now on ;-)) provides only a Finitio-way to make the link between itself and the host language. That makes the definition host-dependent indeed. I think you're right in asking a way to use Finitio the other way round, that is, by making the link on the host side.

It would not require much change in Qrb for the following to work:

```
system = Qrb.parse <<-Q
    Article = <as> { name: String }
Q
system['Article'].ruby_type = MyApp::Model::Article
```

See https://github.com/blambeau/qrb/blob/master/lib/qrb/type/ad_type.rb#L70, all we need is to provide an attr_writer to ruby_type. My only concern here is that I don't want AdType#ruby_type to be part of the public API. So an official and clean way of achieving this must be properly designed.
 

## What exactly is the purpose of ADT?

I think ADTs (and the whole "information contract" thing) sounds
really interesting, but I'm failing to see the value. Why not just do
it like this:

```
ColorTuple = {r: Byte, g: Byte, b: Byte}
ColorHex = String

Color = ColorTuple|ColorHex

Article = {
  color: Color # both are supported
  background_color: ColorTuple # because of legacy reasons, you can
only pass in a tuple here
}
```

Combine this with a Ruby-level API for dressing any Q type into a Ruby
class and it gives you greater flexibility than ADTs do today.

Short answer: this is valid Finitio, you can already do this (except for the dressing part). That is, you can avoid ADTs if you don't want to use them, but you wan't be able to dress without the ADT support. I understand your concern here, but I'm not sure I agree with the conclusion. Let me explain ADTs a bit more.

Finitio is a formal type system: a type is a set of values; a subtype is a subset; a supertype is a superset. Possibly the most important "operator" that a datatype has to provide the user with is a way of answering the question "is this value a member of the set". Let call that operator "is_member?" ADTs allow to distinguish between two very different cases:

```
# one case
UnionColor = {r: Byte, g: Byte, b: Byte} | String
is_member?(UnionColor, { r: 12, g: 14: b: 16 }) # true
is_member?(UnionColor, "...")                   # true

# another one
AdtColor   = <rgb> {r: Byte, g: Byte, b: Byte}
             <hex> String
is_member?(AdtColor, { r: 12, g: 14: b: 16 })   # false
is_member?(AdtColor, "...")                     # false
```

That does not prevent AdtColor from being able to *dress* colors from representations but it does not considers those representations as valid members per se. If you "only" want to validate "low-level" data and document things, that distinction is not that important. But other scenarios require this sharp distinction to be kept.

Now that does not prevent us from finding a nice way to build host-independent systems but will keep that distinction either way!

Bernard

Reply all
Reply to author
Forward
0 new messages