Likewise it can be quite frustrating to be pointed at the proposal as soon as a concern or an idea for mitigation of such concerns comes up when the proposal does not address them.
In this example the idea of making dynamic call sites more visible is only discussed in general in the proposal. The specific suggestion made by @hlovatt and others, to mark expressions with
dyn, which is relatively lightweight, is not discussed at all in the proposal.
Instead the proposal chooses an unnecessary heavyweight method of marking the use sites as an example which is misleading IMHO.
Let me explain why I think that the method chosen as an example in the proposal is unnecessarily heavyweight:
- the chosen
^ sigil for the example is visually intrusive, especially when combined with
^ is a sigil in the upper region of a glyph whereas
. is in the lower region, so they don’t combine well visually)
- the sigil is not only applied to the name lookup but in addition to the call itself which is unnecessary for the concern raised most often: that the dynamic name lookup might fail due to a typo or broken refactoring. It is not a concern that the call itself might fail (I expect here that the dynamic name lookup for a method respects the parameter list for the method, i.e. looking up
foo(1, 2) would fail if only a method
foo(PyVal x) would exist, but not a method
foo(PyVal x, PyVal y)).
Choosing another sigil for dynamic name lookup, i.e.
-> and not applying it to the call itself would then result in the following example which is much more readable. For operators let’s choose a prefix
' (all sigil suggestions are only straw men, of course):
let np = Python.import("numpy")
let x = np->array([6, 7, 8])
let y = np->arange(24)->reshape(2, 3, 4)
let a = np->ones(3, dtype: np->int32)
let b = np->linspace(0, pi, 3)
let c = a '+ b
let d = np->exp(c)
The suggestion of using something similar to
try would look like follows (and is missing from the discussion in the proposal although it has been mentioned several times):
let np = Python.import("numpy")
let x = dyn np.array([6, 7, 8])
let y = dyn np.arange(24).reshape(2, 3, 4)
let a = dyn np.ones(3, dtype: np.int32)
let b = dyn np.linspace(0, pi, 3)
let c = dyn a+b
let d = dyn np.exp(c)
The advantage of this approach is that there is no separate sigil necessary for operators. The proposal states
Requiring additional syntax for
a.b but not
a + b (which can be just as dynamic) would be inconsistent.
This clearly ignores the fact that marking expressions with
dyn (similar to
try) would solve this inconsistency.
Furthermore the whole discussion of whether or not to make call sites more visible is not representing the concerns raised on Swift Evolution well, so let me restate the concern:
- Static type checking ensures that member access or method calls cannot fail due to the member or method not being present on the receiver, i.e. static type checking protects from typos and errors made when renaming things. For dynamic member lookup this is not true anymore and it is not visible where dynamic lookup is used.
IOUs are somewhat of an exception to this rule although even there the member accesses or method calls are checked statically and only fail when the IOU is nil (and in that case all would fail).
The discussion in the proposal rightly notes that
Swift’s type system already includes features (optionals, IUOs, runtime failure) for handling failability.
Adding punctuation to the lookup itself reduces the likelihood that an API author would make the lookups return strong optionals, because of increased syntactic noise.
This might be true but
- something like
dyn would not visually conflict with the
? introduced by strong optionals.
- I do not consider it good design to conflate the failure of member lookup (remember: due to a typo, i.e. a programming error) with a return value being optional because of application logic, i.e. the failures have a different nature.
A similar point raised against marking the use sites of dynamic member lookup is that Swift already contains features that might fail at runtime like array subscripts, integer overflow or IOUs.
IMHO this, too, ignores the fact that these runtime failures have a different nature than failures when looking up a member: the existing features do never fail because of a misspelled name. Of course their failures are programming errors, too, and they might even be the result of typos (e.g. using
+ instead of
-) but inherently they are errors in the program logic (which are generally difficult to detect automatically) whereas lookup failures due to misspelled names can be found quite easily automatically by static type checking.
This failure due to typos is why I do not agree with the claim of type safety made by the proposal. IMHO it is only dynamically type safe but not statically type safe.