Python Interop with Swift 4+

Hi all,

As I mentioned on a couple other threads, I’ve been working on improving Swift interoperability with Python. I have two pitches out: one to improve Python member lookup and one to improve calls to Python values. While those proposals are important, I think it is also useful to see what can be accomplished with Swift today.

To show you how far we can get, here is a Swift playground (tested with Xcode 9) that has an example interoperability layer, and a tutorial for using it. If you’re interested in the pitches, or in Python, please check it out:

PythonInterop.playground.zip (26.5 KB)

3 Likes

Hi Chris,

I’ve had a quick look into how your proposals will allow interop with other dynamic languages. It seems that the model that you chose, where calling a function is a two-step process (getting a DynamicCallable function from a DynamicMemberLookupProtocol type) fits Python like a glove, where everything (including functions) is a PyVal. But in other languages, like Ruby, this model makes less sense.

For example, here is how one uses the Ruby C API to call a method of an object:

result = rb_funcallv(obj, rb_intern(“methodName"), arg_count, args);

And here is how one gets and sets instance variables:

x = rb_iv_get(obj, "@x");
rb_iv_set(obj, "@x", x);

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

If we want a model that fits the most languages, shouldn’t we redefine DynamicCallable to specify the function name?

protocol DynamicCallable {
    associatedtype DynamicCallableArgument
    associatedtype DynamicCallableResult

    func dynamicCall(function: String, arguments: [(String, DynamicCallableArgument)]) throws -> DynamicCallableResult
}

This would work in both languages:

extension PyVal: DynamicMemberLookupProtocol, DynamicCallable {
    func dynamicCall(function: String, arguments: [(String, PyVal)]) throws -> PyVal {
        let functionVal = self[function]
        functionVal.call(arguments)
    }
}

extension RbVal: DynamicMemberLookupProtocol, DynamicCallable {
    func dynamicCall(function: String, arguments: [(String, PyVal)]) throws -> PyVal {
        return rb_funcallv(self, rb_intern(function), arguments.count, arguments);
    }
}

Thoughts?
David.

···

On 20 Nov 2017, at 03:16, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

As I mentioned on a couple other threads, I’ve been working on improving Swift interoperability with Python. I have two pitches out: one to improve Python member lookup and one to improve calls to Python values. While those proposals are important, I think it is also useful to see what can be accomplished with Swift today.

To show you how far we can get, here is a Swift playground (tested with Xcode 9) that has an example interoperability layer, and a tutorial for using it. If you’re interested in the pitches, or in Python, please check it out:

<PythonInterop.playground.zip>

In addition to showing how far the interop can go today (which is really quite far) it shows what the future will look like assuming the member lookup and call issues are resolved. To reiterate what I said on the pitch threads, I am not attached at all to the details of the two pitches themselves, I simply want the problems they address to be solved.

Finally, of course I also welcome questions and feedback on the actual approach, naming, and other suggestions to improve the model! Thanks,

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

Here’s an updated version of this: Most importantly, I found through the implementation effort for DynamicMemberLookupProtocol that it is better for PyVal to be a struct than it is for it to be an existential. This implementation shifts the design to that center of gravity, which makes a lot of things feel more natural:

PythonInterop.playground.zip (26.3 KB)

<snip>

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

I do not believe Ruby does not give you direct external access to variables. Everything with a ‘.’ is a function call.

You would use e.g.

Foo.new.instance_variable_get("@bar”) // => 5

attr :bar exposes a variable @bar through functions bar() and bar=() (and also optimizes storage in some implementations)

-DW

···

On Nov 20, 2017, at 1:16 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

This is correct…sort of. Ruby uses symbols to refer to both methods and instance variables, but instance variable symbols always start with @, so they effectively belong to a different namespace. (Similarly, symbols starting with a capital letter are for constants; symbols starting with @@ are for class variables; I believe symbols starting with $ are for global variables.) Ruby only provides syntax to access another object's methods and (for a class) constants, so in practice there's no way to access another object's instance variables except by calling a method on it, but there's no particular reason our bridge would need to follow that rule.

Leaving aside those technicalities, it's pretty clear that `foo.bar` should access a method, not an instance variable, when `foo` is a Ruby object. That doesn't mean it's the same as Python, though, because in Ruby it will need to *call* the method immediately if we're to provide natural syntax for Ruby objects bridged to Swift.

Bottom line: Ruby cannot be bridged naturally with just an undifferentiated "access member" hook. It needs separate "access property" and "access method" hooks.

···

On Nov 20, 2017, at 12:32 AM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 20, 2017, at 1:16 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

<snip>

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

I do not believe Ruby does not give you direct external access to variables. Everything with a ‘.’ is a function call.

You would use e.g.

Foo.new.instance_variable_get("@bar”) // => 5

attr :bar exposes a variable @bar through functions bar() and bar=() (and also optimizes storage in some implementations)

--
Brent Royal-Gordon
Architechies

For what it’s worth, the Lua Programming language also has a similar C API to Ruby. So it might be worth writing very simple glue code for the most common dynamic languages to see how the DynamicMemberLookupProtocol and DynamicCallable protocols hold up before the proposals are considered for approval.

David.

···

On 20 Nov 2017, at 09:16, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hi Chris,

I’ve had a quick look into how your proposals will allow interop with other dynamic languages. It seems that the model that you chose, where calling a function is a two-step process (getting a DynamicCallable function from a DynamicMemberLookupProtocol type) fits Python like a glove, where everything (including functions) is a PyVal. But in other languages, like Ruby, this model makes less sense.

For example, here is how one uses the Ruby C API to call a method of an object:

result = rb_funcallv(obj, rb_intern(“methodName"), arg_count, args);

And here is how one gets and sets instance variables:

x = rb_iv_get(obj, "@x");
rb_iv_set(obj, "@x", x);

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

If we want a model that fits the most languages, shouldn’t we redefine DynamicCallable to specify the function name?

protocol DynamicCallable {
    associatedtype DynamicCallableArgument
    associatedtype DynamicCallableResult

    func dynamicCall(function: String, arguments: [(String, DynamicCallableArgument)]) throws -> DynamicCallableResult
}

This would work in both languages:

extension PyVal: DynamicMemberLookupProtocol, DynamicCallable {
    func dynamicCall(function: String, arguments: [(String, PyVal)]) throws -> PyVal {
        let functionVal = self[function]
        functionVal.call(arguments)
    }
}

extension RbVal: DynamicMemberLookupProtocol, DynamicCallable {
    func dynamicCall(function: String, arguments: [(String, PyVal)]) throws -> PyVal {
        return rb_funcallv(self, rb_intern(function), arguments.count, arguments);
    }
}

Thoughts?
David.

On 20 Nov 2017, at 03:16, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

As I mentioned on a couple other threads, I’ve been working on improving Swift interoperability with Python. I have two pitches out: one to improve Python member lookup and one to improve calls to Python values. While those proposals are important, I think it is also useful to see what can be accomplished with Swift today.

To show you how far we can get, here is a Swift playground (tested with Xcode 9) that has an example interoperability layer, and a tutorial for using it. If you’re interested in the pitches, or in Python, please check it out:

<PythonInterop.playground.zip>

In addition to showing how far the interop can go today (which is really quite far) it shows what the future will look like assuming the member lookup and call issues are resolved. To reiterate what I said on the pitch threads, I am not attached at all to the details of the two pitches themselves, I simply want the problems they address to be solved.

Finally, of course I also welcome questions and feedback on the actual approach, naming, and other suggestions to improve the model! Thanks,

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

I’ve had a quick look into how your proposals will allow interop with other dynamic languages. It seems that the model that you chose, where calling a function is a two-step process (getting a DynamicCallable function from a DynamicMemberLookupProtocol type) fits Python like a glove, where everything (including functions) is a PyVal. But in other languages, like Ruby, this model makes less sense.

Right, this was also brought up in the DynamicCallable thread, the example given before was Squeak, another Smalltalky language.

It turns out that the right ultimate design for DynamicCallable is probably to have two different entry points: one for direct-function-like calls and one for methods-with-keywords calls, e.g.:

  fn(arg: 1, arg2: 2) // function like
  obj.method(arg: 1, arg2: 2) // method like

It is straight-forward (and fits very very naturally into the Swift call model) to support the second one as an atomic thing, which is I think what you’re getting at. I plan to revise the DynamicCallable proposal to incorporate this directly into the model, but haven’t had time to do so yet.

-Chris

···

On Nov 20, 2017, at 12:16 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

For example, here is how one uses the Ruby C API to call a method of an object:

result = rb_funcallv(obj, rb_intern(“methodName"), arg_count, args);

And here is how one gets and sets instance variables:

x = rb_iv_get(obj, "@x");
rb_iv_set(obj, "@x", x);

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

If we want a model that fits the most languages, shouldn’t we redefine DynamicCallable to specify the function name?

protocol DynamicCallable {
    associatedtype DynamicCallableArgument
    associatedtype DynamicCallableResult

    func dynamicCall(function: String, arguments: [(String, DynamicCallableArgument)]) throws -> DynamicCallableResult
}

This would work in both languages:

extension PyVal: DynamicMemberLookupProtocol, DynamicCallable {
    func dynamicCall(function: String, arguments: [(String, PyVal)]) throws -> PyVal {
        let functionVal = self[function]
        functionVal.call(arguments)
    }
}

extension RbVal: DynamicMemberLookupProtocol, DynamicCallable {
    func dynamicCall(function: String, arguments: [(String, PyVal)]) throws -> PyVal {
        return rb_funcallv(self, rb_intern(function), arguments.count, arguments);
    }
}

Thoughts?
David.

On 20 Nov 2017, at 03:16, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

As I mentioned on a couple other threads, I’ve been working on improving Swift interoperability with Python. I have two pitches out: one to improve Python member lookup and one to improve calls to Python values. While those proposals are important, I think it is also useful to see what can be accomplished with Swift today.

To show you how far we can get, here is a Swift playground (tested with Xcode 9) that has an example interoperability layer, and a tutorial for using it. If you’re interested in the pitches, or in Python, please check it out:

<PythonInterop.playground.zip>

In addition to showing how far the interop can go today (which is really quite far) it shows what the future will look like assuming the member lookup and call issues are resolved. To reiterate what I said on the pitch threads, I am not attached at all to the details of the two pitches themselves, I simply want the problems they address to be solved.

Finally, of course I also welcome questions and feedback on the actual approach, naming, and other suggestions to improve the model! Thanks,

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

<snip>

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

I do not believe Ruby does not give you direct external access to variables. Everything with a ‘.’ is a function call.

You would use e.g.

Foo.new.instance_variable_get("@bar”) // => 5

attr :bar exposes a variable @bar through functions bar() and bar=() (and also optimizes storage in some implementations)

This is correct…sort of. Ruby uses symbols to refer to both methods and instance variables, but instance variable symbols always start with @, so they effectively belong to a different namespace. (Similarly, symbols starting with a capital letter are for constants; symbols starting with @@ are for class variables; I believe symbols starting with $ are for global variables.) Ruby only provides syntax to access another object's methods and (for a class) constants, so in practice there's no way to access another object's instance variables except by calling a method on it, but there's no particular reason our bridge would need to follow that rule.

Leaving aside those technicalities, it's pretty clear that `foo.bar` should access a method, not an instance variable, when `foo` is a Ruby object. That doesn't mean it's the same as Python, though, because in Ruby it will need to *call* the method immediately if we're to provide natural syntax for Ruby objects bridged to Swift.

Exactly. My example was a bit contrived but that’s what I wanted to say.

···

On 20 Nov 2017, at 12:34, Brent Royal-Gordon <brent@architechies.com> wrote:

On Nov 20, 2017, at 12:32 AM, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 20, 2017, at 1:16 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Bottom line: Ruby cannot be bridged naturally with just an undifferentiated "access member" hook. It needs separate "access property" and "access method" hooks.

--
Brent Royal-Gordon
Architechies

For anyone interested, here’s another update on this, including a bunch of operators, conformances to standard Swift protocols etc.

PythonInterop.playground.zip (28 KB)

1 Like

<snip>

Moreover, Ruby allows classes to have instance variables with the same name as methods:

class Foo
  def initialize()
    @bar = 5
  end

  def bar()
    puts “Hello"
  end
end

In that case, how would one implement DynamicMemberLookupProtocol for the lookup of bar, and what would the return value be? Its entirely context sensitive.

I do not believe Ruby does not give you direct external access to variables. Everything with a ‘.’ is a function call.

You would use e.g.

Foo.new.instance_variable_get("@bar”) // => 5

attr :bar exposes a variable @bar through functions bar() and bar=() (and also optimizes storage in some implementations)

This is correct…sort of. Ruby uses symbols to refer to both methods and instance variables, but instance variable symbols always start with @, so they effectively belong to a different namespace. (Similarly, symbols starting with a capital letter are for constants; symbols starting with @@ are for class variables; I believe symbols starting with $ are for global variables.) Ruby only provides syntax to access another object's methods and (for a class) constants, so in practice there's no way to access another object's instance variables except by calling a method on it, but there's no particular reason our bridge would need to follow that rule.

Just to add a little weight to what Brent wrote here: in Ruby, one can •only• access instance variables of self using the @bar syntax. There is no such thing as “foo.@bar”; as Brent wrote, one can only access another object — not even another class, but another object! — via its methods.

Class authors thus expect @bar to be private (well, same-instance protected), and instance_variable_get/set is the accepted in-Ruby hack to fiddle with another object’s internal state. I see no reason a bridge would need to deviate from that understanding.

Leaving aside those technicalities, it's pretty clear that `foo.bar` should access a method, not an instance variabl, when `foo` is a Ruby object. That doesn't mean it's the same as Python, though, because in Ruby it will need to *call* the method immediately if we're to provide natural syntax for Ruby objects bridged to Swift.

Exactly. My example was a bit contrived but that’s what I wanted to say.

X-ref to a thread where we discussed this at greater length:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171113/041447.html

Cheers,

Paul

···

On Nov 20, 2017, at 7:08 AM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 20 Nov 2017, at 12:34, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

On Nov 20, 2017, at 12:32 AM, David Waite via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 20, 2017, at 1:16 AM, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What if you write ‘let fn = obj.method’?

Slava

···

On Nov 20, 2017, at 1:39 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

It is straight-forward (and fits very very naturally into the Swift call model) to support the second one as an atomic thing, which is I think what you’re getting at.

That’s related to the DynamicMemberLookup proposal. I’m not familiar with Ruby, but it sounds like the implementation would end up calling rb_iv_get/set to manipulate instance variables. Is that your question or are you asking something else?

-Chris

···

On Nov 20, 2017, at 10:50 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 20, 2017, at 1:39 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

It is straight-forward (and fits very very naturally into the Swift call model) to support the second one as an atomic thing, which is I think what you’re getting at.

What if you write ‘let fn = obj.method’?

In ruby, parens are optional. So,

v = foo.value

and

v = foo.value()

are identical. There dot syntax is only used for method invocation, so there is no external access to instance variables without some twiddling; similarly getting access to a Proc/lambda/Method requires twiddling in Ruby (although there are shortcuts in normal use, like Symbol#to_proc)

Ruby class implementations often expose a getter and/or setter for instance variables - under the function names example_value() and example_value=(). If instance variables/class variables are not exposed in this manner, then the expectation is that you weren’t meant to see/manipulate them, and do so at your own peril.

For mapping to Swift, I would say that parens are needed; we can’t guess whether a `foo.bar` is meant to be asking for the value of attribute bar or a reference to method bar.

More difficult would be the use of ‘=‘, ‘!’, and ‘?’ - all legal in Ruby method names as suffixes. ‘=‘ as already stated is used for setters, ‘!’ for differentiating mutating operations, and ‘?’ to indicate the result is boolean (or sometimes just ‘truthy’ , as every object other than false and nil evaluate to true)

I could imagine tricks to make `foo.bar = 1` work, but I’m not sure what would be an appropriate way to represent ! and ? methods.

-DW

···

On Nov 20, 2017, at 1:10 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 20, 2017, at 10:50 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 20, 2017, at 1:39 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

It is straight-forward (and fits very very naturally into the Swift call model) to support the second one as an atomic thing, which is I think what you’re getting at.

What if you write ‘let fn = obj.method’?

That’s related to the DynamicMemberLookup proposal. I’m not familiar with Ruby, but it sounds like the implementation would end up calling rb_iv_get/set to manipulate instance variables. Is that your question or are you asking something else?

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

It is straight-forward (and fits very very naturally into the Swift call model) to support the second one as an atomic thing, which is I think what you’re getting at.

What if you write ‘let fn = obj.method’?

That’s related to the DynamicMemberLookup proposal. I’m not familiar with Ruby, but it sounds like the implementation would end up calling rb_iv_get/set to manipulate instance variables. Is that your question or are you asking something else?

I don’t think that’s what he is asking. If `method` is indeed a method, then `obj.method` in Ruby would return the method as a `Proc` (If I’m not mistaken), ready to be called, very similarly to how it works in Swift:

class Foo {
    func bar(_ a: String) {
        print(a)
    }
}

let foo = Foo()
let b = foo.bar
b()

···

On 20 Nov 2017, at 21:10, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Nov 20, 2017, at 10:50 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 20, 2017, at 1:39 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Since Ruby doesn't distinguish between properties and methods, I would write the `subscript(dynamicProperty:)` getter to be equivalent to a zero-argument method call. This is a pragmatic tradeoff—property access is *way* more common than uncalled method access.

I assume that Swift would send `rubyObject.someMethod(_:_:)` through a method-related call, not a property-related one. Having a way to unambiguously specify a zero-argument method would allow the uncalled syntax to be used with Ruby methods which would otherwise be interpreted as properties.

(The setter on properties would handle the problem of Ruby's `=` methods. As for `?` and `!`, unless we extend the `identifier` syntax to allow non-identifier characters, I'd actually be tempted to bridge them with `is` and `unsafe` prefixes respectively. That might be a little too cute, though.)

···

On Nov 20, 2017, at 10:50 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

What if you write ‘let fn = obj.method’?

--
Brent Royal-Gordon
Architechies

In ruby, parens are optional. So,

v = foo.value

and

v = foo.value()

are identical.

Ok, I wasn’t aware of that. It isn’t clear that we’d want to carry that into a “Ruby APIs when used in Swift” though! One could definitely argue against the former calling a method, even if that is possible in Ruby APIs.

There dot syntax is only used for method invocation, so there is no external access to instance variables without some twiddling; similarly getting access to a Proc/lambda/Method requires twiddling in Ruby (although there are shortcuts in normal use, like Symbol#to_proc)

I think you’re missing the idea here: the idea isn’t to provide exactly syntax mapping of Ruby (or Python) into Swift, it is to expose the underlying semantic concepts in terms of Swift’s syntax. In the case of Python, there is a lot of direct overlap, but there is also some places where Swift and Python differs (e.g. Python slicing syntax vs Swift ranges). In my opinion, Swift syntax wins here, we shouldn’t try to ape a non-native syntax in Swift.

For mapping to Swift, I would say that parens are needed; we can’t guess whether a `foo.bar` is meant to be asking for the value of attribute bar or a reference to method bar.

+1

More difficult would be the use of ‘=‘, ‘!’, and ‘?’ - all legal in Ruby method names as suffixes.

Using those would require backquotes:

x.`what?`()

-Chris

···

On Nov 20, 2017, at 1:41 PM, David Waite <david@alkaline-solutions.com> wrote:

I’m not going to speculate what Slava meant (please speak up!), but given David Waite’s recent email, it isn’t clear that we’d want to provide this. It seems reasonable for a Ruby interop layer to implement the DynamicCallable (in method only form?) but not DynamicMemberLookup.

-Chris

···

On Nov 20, 2017, at 12:50 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

On 20 Nov 2017, at 21:10, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 20, 2017, at 10:50 AM, Slava Pestov via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Nov 20, 2017, at 1:39 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

It is straight-forward (and fits very very naturally into the Swift call model) to support the second one as an atomic thing, which is I think what you’re getting at.

What if you write ‘let fn = obj.method’?

That’s related to the DynamicMemberLookup proposal. I’m not familiar with Ruby, but it sounds like the implementation would end up calling rb_iv_get/set to manipulate instance variables. Is that your question or are you asking something else?

I don’t think that’s what he is asking. If `method` is indeed a method, then `obj.method` in Ruby would return the method as a `Proc` (If I’m not mistaken), ready to be called, very similarly to how it works in Swift:

class Foo {
    func bar(_ a: String) {
        print(a)
    }
}

let foo = Foo()
let b = foo.bar
b()

<snip>

I think you’re missing the idea here: the idea isn’t to provide exactly syntax mapping of Ruby (or Python) into Swift, it is to expose the underlying semantic concepts in terms of Swift’s syntax. In the case of Python, there is a lot of direct overlap, but there is also some places where Swift and Python differs (e.g. Python slicing syntax vs Swift ranges). In my opinion, Swift syntax wins here, we shouldn’t try to ape a non-native syntax in Swift.

Just wanted to point out Ruby language rules. For swift, you’d probably want to have property-style accessors always return something akin to a function pointer.

<snip>

More difficult would be the use of ‘=‘, ‘!’, and ‘?’ - all legal in Ruby method names as suffixes.

Using those would require backquotes:

x.`what?`()

Ruby attributes syntax does wind up looking a bit ugly there. For `bar` on class `Foo` in ruby,

x.foo() # return value of attribute foo
x.`foo=`(5) # assign value of foo as 5

Not pretty, but it should work. All the approaches I’ve been thinking up to improve that wind up being pretty nonintuitive and fragile.

-DW

Links to attachments don't seem to work, can you please reattach the latest playground.

Apparently we cannot upload .zip files to this forum, I get an error of: "Sorry, the file you are trying to upload is not authorized (authorized extensions: jpg, jpeg, png, gif)."

3 Likes