FWIW
> const foo: AnyPointer = "hello";
Parsing this could be tricky because the literal "hello" isn't necessarily
a Text; it could be a Data. This is why, in general, you can only assign an
AnyPointer value to a named constant, because that named constant has an
explicit type. That is:
> const fooText :Text = "foo";
> const foo :AnyPointer = .fooText;
However, it appears that this doesn't work either, currently. That seems
like a bug. This does work:
> struct Foo {}
> const fooTyped :Foo = ();
> const foo :AnyPointer = .fooTyped;
As to the original question, again, I think what Kuba is trying to do ought
to work.
And as to what's allowed at runtime, it seems reasonable that we could
support covariance on struct-typed setters. Since setters make a copy,
there is no soundness concern.
I am also not religious about soundness. I prioritize practicality.
-Kenton
On Thu, Sep 19, 2019 at 2:46 PM Ian Denhardt <[email protected]> wrote:
> 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 [email protected] (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][email protected]>:
> > > >
> > > > 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][email protected].
> > 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:[email protected]
> > 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 [email protected].
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/capnproto/156892956443.821.23146561515920431%40localhost.localdomain
> .
>
--
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 [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/capnproto/CAJouXQk92wOnduR3weTMZYyg3AZ1Gr8ntq-_BzZ7ibOsk7NAcA%40mail.gmail.com.