A thing I'd missed before, since we're just talking about constants, my
concern about multiple pointers (of different types) in the program
can't actually happen as long as we don't alias within the message --
since these aren't messages being constructed at runtime. So I think
Kenton is actually right that allowing covariant generics for constants
in the schema file is fine as long as we don't alias within a message.

Regarding what the rules should be around `AnyPointer`: I think it's
reasonable to allow otherwise-possibly-unsound assignments of
`AnyPointer`s with explicit casts, but I don't think such things
should just silently succeed.

In principle, this would be fine:

```
const foo: AnyPointer = "hello";
```

Since `Text` is always valid to use as a misc. pointer.

But this should fail:

```
const bar: AnyPointer = ...;
const baz: Text = bar; # Failure should happen here.
```

Because the inverse isn't true -- we don't know statically that `bar` is
actually usable as a `Text`.

Right now the first case is actually rejected -- maybe it shouldn't be.

The Java example is only relevant to mutable cases, but the point is it
demonstrates how allowing covariant subtyping on mutable generic types
leads to the same problem. It's not about how stuff that doesn't get
caught statically is handled at runtime, it's about what gets caught
statically. This was flatly just a mistake in the type system, and
the generics added to Java later do not work the same way in this regard
as arrays, since they'd discovered the mistake by the time they added
them.

Quoting k...@bertec.com (2019-09-19 16:29:47)
>    But I'm talking about Capnproto schema language, not any particular
>    implementation. If catching mistakes were a viable goal, then there
>    would be no `AnyPointer`, since that is (and I quote documentation)
>    "void* like in C". Can't get any more explicit than that: `void*`
>    catches no mistakes, it's the idiom for type erasure, it lets you write
>    type-unsound programs. Same goes for generic interfaces and generic
>    methods - no typechecking there in the sense that either end of the RPC
>    connection can use wrong types and everything can then blow up, with no
>    runtime diagnostics other than when the pointer kinds mismatch.
>    As things are, it's impossible to divine when `AnyPointer` is usable
>    and when it isn't. The usual way the implementations deal with going
>    from `AnyPointer` to some specific type is by doing an explicit cast,
>    just as you would in C, letting the user assert that they know what
>    they are doing. I really fail to see what the Java snippet provided
>    below has to do with this Capnproto issue. In Java, `Object` is like
>    `void*`, it's a type-erasure type, and so are `object` and `dynamic` in
>    C#. If you want to do silly things, the runtime will stop you, and
>    that's that, no problem - you chose to use type erasure, you reap what
>    you sow. In C, C++ and Capnproto, the runtime won't generally stop you,
>    and that's fine with me, too. You can initialize whatever pointer type
>    you want in an `AnyPointer` field, and the user of the structure must
>    somehow divine what really went there.
>    So for there to be some coherency, either `AnyPointer` has to be
>    removed, or it has to be embraced to mean what it means in C/C++: type
>    erasure and delegation of type checking to the user, not the compiler.
>    It's a broken typecheck escape as it stands right now. If typechecks
>    were not to be bypassed, then not only would `AnyPointer` have to go,
>    but interface specializations would need to do typechecking on the
>    wire, so that at least at runtime the mistakes would be caught.
>    If `AnyPointer` is not a stand-in for `void*`, then a) this mention has
>    to go from the documentation, since neither design intent nor reference
>    implementation behavior are really like `void*`, and b) some guidance
>    has to be provided as to what purpose does `AnyPointer` serve, since
>    clearly generally taken type erasure isn't its goal. Except the few
>    times it is, of course. Sigh.
>    Looking past AnyPointer: You can specialize Capnproto generic
>    interfaces to any pointer type you want on either side of the wire, and
>    so you can specialize generic methods too, and it's entirely to the
>    user and the interface implementor to somehow agree on what types are
>    really passed around. There are no typesystem checks for this, and the
>    parameter and result wire formats can differ, etc. RPC typechecking is
>    quite arbitrary in general: the unspecialized interface type is
>    typechecked on every method call, as a consequence of an implementation
>    detail (a sensible implementation detail, I should add), yet the lower
>    hanging fruit of typechecking a generic interface's type parameters at
>    capability acquisition time is not done, even though its wire overhead
>    would be dwarfed by the cost of method invocations (including their
>    inherent typechecking).
>    Looking even further, this arbitrariness carries to constants, where
>    you can't do what is perfectly fine at runtime - and if one wasn't
>    confused already, this is a real stunner. At runtime you can e.g.
>    initialize an `AnyPointer` field as `Text`, but constants don't let you
>    do that. I have no idea in fact what was the intended behavior of an
>    `AnyPointer` field in a constant. This puzzles me to no end given that
>    type soundness checking lends itself well to compile-time typechecking
>    of constants. If `AnyPointer` is verboten where typechecking would be
>    impossible, then surely it should be allowed in constant context where
>    typechecking can be done. Imagine that in const context you replace
>    every `AnyPointer` field with a generic type parameter, and then
>    substitute the actual types used, and present the constant of such a
>    type to the user: it's wire-compatible with `AnyPointer`, and it's a
>    minor matter to allow conversion from such a specialized generic type
>    to a non-generic type with `AnyPointer` substituted for each generic
>    parameter. Hey, the generic unspecialized type already acts as if it
>    had `AnyPointer` type parameters, so this is like 99% done, the only
>    missing step is conversion between `struct Generic(T) { field @0 :T; }`
>    and `struct NonGeneric { field @0 :AnyPointer; }` Maybe `AnyPointer`
>    should not be available, and instead one should be forced to use type
>    parameters wherever one would use `AnyPointer`? Would that be more
>    kosher? Or perhaps structs with `AnyPointer` fields should be
>    transcribed to generic types (effectively being some weird syntactic
>    sugar), with `AnyPointer` really meaning "an unnamed generic type
>    parameter"?
>    The more I look into this, the more arbitrary it all seems to be, and I
>    can't visualize overarching design goals that might have driven this.
>    Now I do appreciate that implementation realities often curtail fully
>    developed designs, so I'm not trying to imply that the present way
>    Capnproto works is somehow inherently "bad" - it is what it is, and the
>    only way to look is forward, AFAICT.
>    Cheers, Kuba
>    On Tuesday, September 17, 2019 at 6:13:15 PM UTC-4, Ian Denhardt wrote:
>
>      (Adding the list back to CC; I assume you didn't mean to just send
>      this
>      to me).
>      > When passing GenericType to something that expects
>      GenericType(Text),
>      > it�s up to the user not to mess it up
>      Catching this kind of mistake is the whole point of a type system.
>      If
>      you're going to make the argument that the type system shouldn't
>      worry
>      too much about edge cases and just act as a linter, then maybe you
>      can
>      claim that this isn't a big deal, but I think the premise that
>      Kenton
>      and I have been assuming is that type soundness (the property that
>      well-typed programs do not have run-time type errors) is desirable
>      here.
>      Obviously this isn't really achievable for the C++ implementation
>      overall since C++ itself fails this property, but it's probably
>      worth
>      hanging on to both for other languages and because getting closer to
>      the
>      goal in C++ is probably still a useful thing.
>      ---
>      To get into the details of what the problem is: perhaps this is
>      review
>      for everyone, but: the classic example of the problem with covariant
>      generics and mutability is demonstrated by this java program:
>      �  �  public class Main {
>      �  �  �  �  public static void main(String[] args) {
>      �  �  �  �  �  �  Integer[] ints = new Integer[4];
>      �  �  �  �  �  �  // assign by reference, so `objs` points to the
>      same array
>      �  �  �  �  �  �  // as `ints`. Covariance (the notion that if A is
>      a subtype
>      �  �  �  �  �  �  // of B, then A[] is a subtype of B[]) is rule by
>      which java
>      �  �  �  �  �  �  // admits this statement:
>      �  �  �  �  �  �  Object[] objs = ints;
>      �  �  �  �  �  �  // And then because String is a subtype of Object,
>      we can
>      �  �  �  �  �  �  // put a string in our list of integers through
>      `objs`:
>      �  �  �  �  �  �  objs[0] = "OOPS";
>      �  �  �  �  }
>      �  �  }
>      As Kenton suggests, the example critically depends on pointer
>      aliasing
>      for its unsoundness, so given that such aliasing is banned by the
>      spec,
>      it may not be possible to construct such an example in a given
>      message.
>      However, per my prior email it's not clear that you can't still run
>      into
>      trouble by aliased references to the root struct of a message from
>      the
>      rest of the program.
>      -Ian
>      Quoting Kuba Ober (2019-09-17 17:44:37)
>      > Either I�m not getting something or this is certainly meant to
>      work with mutable types?
>      >
>      > Any field accepting a GenericType should accept an arbitrary
>      specialization, at least in the implementations I know of. Of course
>      the application itself may further constrain what types are allowed,
>      but we�re talking about static type checking within CapnProto
>      runtime implementation(s).
>      >
>      > I consider implementations that would not allow it to be buggy �
>      otherwise the entire premise of generic types in CapnProto is IMHO
>      broken. As far as I can divine intent from CapnProto documentation,
>      the generics were designed so that an unparametrized type is a
>      stand-in for all of its specializations, both in co- and
>      contravariant directions. When passing GenericType to something that
>      expects GenericType(Text), it�s up to the user not to mess it up �
>      there are several such areas in CapnProto where the sender of a
>      message and the receiver must agree on what type is actually sent.
>      >
>      > It�s up to the implementer to make it possibly type-safe, e.g. an
>      implementation could store the parameter type id and do a single
>      check when GenericType.Reader is coerced to
>      GenericType<Text>.Reader, and so on.
>      >
>      > Slightly confused, Kuba
>      >
>      > > 16 sep. 2019 kl. 6:31 em skrev Ian Denhardt
>      <[1]i...@zenhack.net>:
>      > >
>      > > Quoting 'Kenton Varda' via Cap'n Proto (2019-09-16 16:14:59)
>      > >
>      > >> �  Anyway, I guess given that there's no such thing as a
>      constant
>      > >> �  capability currently, we don't need to worry about that? And
>      covariance
>      > >> �  is correct for all other types? So we could support it?
>      > >
>      > > It's sound for constants, but given that it's not for mutable
>      values
>      > > (even without caps), my gut is that adding this is probably not
>      a good
>      > > cost:benefit ratio. It would only enable creating constants that
>      would
>      > > be impossible to construct dynamically anyway, and it's not
>      clear to me
>      > > what sort of programming this enables that justifies that.
>      > >
>      > > -Ian
>
>    --
>    You received this message because you are subscribed to the Google
>    Groups "Cap'n Proto" group.
>    To unsubscribe from this group and stop receiving emails from it, send
>    an email to [2]capnproto+unsubscr...@googlegroups.com.
>    To view this discussion on the web visit
>    [3]https://groups.google.com/d/msgid/capnproto/29333b57-9383-4535-9949-
>    e0ca0bff5c96%40googlegroups.com.
>
> Verweise
>
>    1. javascript:/
>    2. mailto:capnproto+unsubscr...@googlegroups.com
>    3. 
> https://groups.google.com/d/msgid/capnproto/29333b57-9383-4535-9949-e0ca0bff5c96%40googlegroups.com?utm_medium=email&utm_source=footer

-- 
You received this message because you are subscribed to the Google Groups 
"Cap'n Proto" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to capnproto+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/capnproto/156892956443.821.23146561515920431%40localhost.localdomain.

Reply via email to