Proposal: Introduce User-defined "Dynamic Member Lookup" Types

Hi

I’ve very much been teetering on my preference of having dynamic member lookup sites marked or not and have landed on the side of having it marked. This is for a few reasons:

1. There needs to be some way of conveying that this is not a ‘blessed’ way of writing things.
2. It’s a different paradigm and would benefit a reader by having that documented.
3. It’s weird that code completion, jump to and other code insights stop working for valid members. I’m not worried about having these insights not working though, just the inconsistency. Along with my mental model of my code not working with my tools meaning theres something wrong with my code.

The most minimal way of marking expressions in a way I could think of that would also remove anyones notion of "this code is fully supported static Swift code” would be to use the currently reserved ‘ (Single quote).

Using the code example from another conversation:

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)
print(d)

Would become:

let np = Python.import("numpy")
let x = 'np.array([6, 7, 8])'
let y = 'np.arrange(24).reshape(2, 3, 4)'

let a = 'np.ones(3, dtype: np.int32)'
let b = 'np.linespace(0, pi, 3)'
let c = a+b
let d = 'np.exp(c)’
print(d)

If that is still too heavy a single quote only at the beginning of the expression could be used:

let np = Python.import("numpy")
let x = 'np.array([6, 7, 8])
let y = 'np.arrange(24).reshape(2, 3, 4)

let a = 'np.ones(3, dtype: 'np.int32)
let b = 'np.linespace(0, pi, 3)
let c = a+b
let d = 'np.exp(c)
print(d)

I’m aware that Python is a dynamic language and would be fine with not having lookups marked if it were just dynamic language wrappers that this proposal affected. A way to get around this could be to use Option 2 of "Reducing Potential Abuse” in the proposal along with having non blessed types require markings or would that be too much of an inconsistency?

FWIW I really hope this proposal goes through, even if it were as is, just for the reason of getting Swift more people.
Another reason I have to wanting this dynamic feature to be more explicit is that I (like I suspect many others) have been burnt by dynamic features in the past and have run to Swift as a home away from it :)

-Letanyan

1. a+b is also is a dynamic method call in this example. How would you explain to a programmer that they have to use single quotes around some expressions but not others?
2. Having this kind of special syntax would feel very arbitrary to the programmer, and hard to look up. Fixits would of course be able to help here, but if the compiler already knows what you should write, why does it require it? Which brings me to my third point:
3. This feels like punishment, like Python making a special syntax for static language bridges where they have to use the phrase "static_typing_is_for_losers" in front of every method call.

It's not like dynamic method calls will appear in your code if you don't use these kind of bridges, and even if you do, it will only be when you call methods on that bridge's types. When you call a method on an object, you already have to know what that object does, or you will be in trouble.

/Magnus

···

4 Dec. 2017 11:17 Letanyan Arumugam via swift-evolution <swift-evolution@swift.org> wrote:

The most minimal way of marking expressions in a way I could think of that would also remove anyones notion of "this code is fully supported static Swift code” would be to use the currently reserved ‘ (Single quote).

Using the code example from another conversation:

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)
print(d)

Would become:

let np = Python.import("numpy")
let x = 'np.array([6, 7, 8])'
let y = 'np.arrange(24).reshape(2, 3, 4)'

let a = 'np.ones(3, dtype: np.int32)'
let b = 'np.linespace(0, pi, 3)'
let c = a+b
let d = 'np.exp(c)’
print(d)

I find it very hard to believe that Swift libraries are going to end up exposing this as a public api en masse. And if you inherit a code base where it's used internally, then it's fully within your power to wrap these dynamic calls in more strongly typed wrappers (you could even do this with third party libraries if necessary). You could just as easily inherit a code base where force unwrapping or IUOs are used irresponsibly, but this doesn't mean that these language features have no legitimate use. The solution is to refactor the code to use these idioms in the correct way.

···

On Dec 3, 2017, at 11:35 PM, Letanyan Arumugam via swift-evolution <swift-evolution@swift.org> wrote:

I‘m not always the only one writing code (I could inherit a code base). I’ve used languages with only dynamic lookup before and knowing about something doesn’t always help especially when the documentation isn’t clear. Which is very possible in this case were libraries could be made by third parties.

1. a+b is also is a dynamic method call in this example. How would you explain to a programmer that they have to use single quotes around some expressions but not others?

Operators already have dynamic behaviour with operator overloads. Operators like + fail without warning as well, this is precedent in the language. Swift has decided to make the default for operators simple while safer usage comes at the cost of more explicit operators: &+ and the likes

2. Having this kind of special syntax would feel very arbitrary to the programmer, and hard to look up. Fixits would of course be able to help here, but if the compiler already knows what you should write, why does it require it? Which brings me to my third point:

For the reasons I mention above. Yes it’s arbitrary but it conveys meaning just like ! conveys meaning.

"let x: Int” vs “let x: Int!” have vastly different behaviours

3. This feels like punishment, like Python making a special syntax for static language bridges where they have to use the phrase "static_typing_is_for_losers" in front of every method call.

Getting Python into the language is a goal for this proposal not the proposal. The actual proposal has far reaching changes. Theres a reason why we don’t explicitly let any string subscripts of an object be accepted as members on objects like JavaScript.

I have nothing against Python and think it would be fine without any markings as I mention in the end of my last email. This is because it has a precedent of being dynamic and anyone choosing to use it knowns exactly what they’re in for. Choosing to use Python in Swift would be a calculated decision by someone with prior experience in Python. Anyone coming into a Swift codebase with Python should be made aware of this before accepting the task (for example in a job description, requires Swift and Python knowledge).

However for things that are not obvious that they should be dynamic should tell the reader that what I’m doing here is different from just getting members from my well defined object. I’m accessing dynamic information which by its very nature is dynamic and might exist in some situations or might change over time. I need this information to be sure that when I’m debugging that the pitfalls of dynamic members are made aware to me. You might say that's what protocol conformance tells me. But right now I’m debugging and I don’t know what's going to trigger my aha moment. Constant reminders of here lie dragons of dynamic code certainly helps.

It's not like dynamic method calls will appear in your code if you don't use these kind of bridges, and even if you do, it will only be when you call methods on that bridge's types. When you call a method on an object, you already have to know what that object does, or you will be in trouble.

I‘m not always the only one writing code (I could inherit a code base). I’ve used languages with only dynamic lookup before and knowing about something doesn’t always help especially when the documentation isn’t clear. Which is very possible in this case were libraries could be made by third parties.

The whole point of this proposal is to get more people into Swift. People who are from a very dynamic field so who’s to say what path new library vendors will go down. Getting people into Swift that only end up working in this dynamic world that is on par with our current world would serve no benefit to anyone.

Is it really as likely to inherit a code base that uses features that from day one Swift made clear was not the ideal way to go. Versus something that has nothing to differentiate it from “normal" Swift members.

What it means to be “normal" here could be a sticking point between us because I don’t think dynamic member lookups should be a thing that is common place to do and should be discouraged in some way not a lot but enough to make people think I should do this differently.

···

On 04 Dec 2017, at 07:47, Joe DeCapo <snoogansbc@gmail.com> wrote:

On Dec 3, 2017, at 11:35 PM, Letanyan Arumugam via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I‘m not always the only one writing code (I could inherit a code base). I’ve used languages with only dynamic lookup before and knowing about something doesn’t always help especially when the documentation isn’t clear. Which is very possible in this case were libraries could be made by third parties.

I find it very hard to believe that Swift libraries are going to end up exposing this as a public api en masse. And if you inherit a code base where it's used internally, then it's fully within your power to wrap these dynamic calls in more strongly typed wrappers (you could even do this with third party libraries if necessary). You could just as easily inherit a code base where force unwrapping or IUOs are used irresponsibly, but this doesn't mean that these language features have no legitimate use. The solution is to refactor the code to use these idioms in the correct way.