Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Possible generics issues / limitations: Type param names, subtyping #2881

Open
patternspandemic opened this issue Sep 17, 2018 · 2 comments
Open

Comments

@patternspandemic
Copy link
Contributor

Please forgive my possibly confused language around the topic of generic typing. Hopefully the example will provide clarity. In my attempt to solve my issue, I've come across some interesting behavior by the compiler which may be an issue or limitation with Pony's generics.

The below example (run on playground) demonstrates my intention of being able to map a trivialized event stream of type USize to another of type String.

trait Events[T: Any #share]
  """ Assume `Events` represents a stream of events of type `T`. """

  // For simplification
  fun ref set_event(e: T)
  fun ref get_event(): T

  // Maps this event stream of type `T` to another stream of type `R`
  fun ref map[R: Any #share](
    f: {(T): R}) // A lambda which transforms T to R
    : Events[R]
  =>
    _MappedEvents[T, R](this, f)


// This version of `_MappedEvents` shares type parameter names `T` and `R`
// with the supertype it provides.
class _MappedEvents[T: Any #share, R: Any #share] is Events[R]
  """ An event stream which transforms events of `_from` with `_f` """
  let _from: Events[T]
  let _f: {(T): R}
  
  new create(
    from: Events[T],
    f: {(T): R})
  =>
    _from = from
    _f = f
    
  fun ref set_event(e: R) => None
  fun ref get_event(): R =>
    let e: T = _from.get_event()
    _f(e)

/*
// This version of `_MappedEvents` uses unique type parameter names.
class _MappedEvents[A: Any #share, B: Any #share] is Events[B]
  """ An event stream which transforms events of `_from` with `_f` """
  let _from: Events[A]
  let _f: {(A): B}
  
  new create(
    from: Events[A],
    f: {(A): B})
  =>
    _from = from
    _f = f
    
  fun ref set_event(e: B) => None
  fun ref get_event(): B =>
    let e: A = _from.get_event()
    _f(e)
*/


actor Main
  new create(env: Env) =>

    // Construct an event stream of USize
    let usize_events: Events[USize] =
      object ref is Events[USize]
        var _event: USize = 0
        fun ref set_event(n: USize) => _event = n
        fun ref get_event(): USize => _event
      end

    // Set the event on the 'stream'
    usize_events.set_event(7)

    // Map `usize_events` to a new event stream `string_events` of type String
    let string_events: Events[String] = usize_events.map[String]({(n: USize): String => n.string()})

    // Get and print the mapped 'event' of 7 to "7"
    env.out.print(string_events.get_event())

The resulting error has been described as odd in that some type parameter names may not have been reified to provided types, for example USize or String (See also #2867). I do not understand the generic compilation process enough to know whether this is the case:

0.24.4 [release]
compiled with: llvm 3.9.1 -- cc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
Defaults: pic=false ssl=openssl_0.9.0
Error:
main.pony:19:1: type does not implement its provides list
class _MappedEvents[T: Any #share, R: Any #share] is Events[R]
^
    Info:
    main.pony:10:18: Any #share is not a subtype of R #share: the type parameter has no lower bounds
      fun ref map[R: Any #share](
                     ^
    main.pony:11:8: parameter R #share is not a supertype of R #share
        f: {(T): R}) // A lambda which transforms T to R
           ^
    main.pony:11:8: {(T): R}[R #share, R #share] ref is not a subtype of {(T): R}[R #share, R #share] ref: method 'apply' has an incompatible signature
        f: {(T): R}) // A lambda which transforms T to R
           ^
    main.pony:10:3: parameter {(T): R}[R #share, R #share] ref is not a supertype of {(T): R}[R #share, R #share] ref
      fun ref map[R: Any #share](
      ^
    main.pony:10:3: _MappedEvents[T #share, R #share] ref is not a subtype of Events[R #share] ref: method 'map' has an incompatible signature
      fun ref map[R: Any #share](
      ^

In my attempt to get around the error, I decided to use unique type parameter names in _MappedEvents (see alternate implementation above, run in playground), which resulted in a different compilation error as shown:

Error:
main.pony:14:25: argument not a subtype of parameter
    _MappedEvents[T, R](this, f)
                        ^
    Info:
    main.pony:14:25: argument type is _MappedEvents[A #share, B #share] ref
        _MappedEvents[T, R](this, f)
                            ^
    main.pony:44:5: parameter type is Events[R #share] ref
        from: Events[A],
        ^
    main.pony:14:25: _MappedEvents[A #share, B #share] ref does not implement trait Events[R #share] ref
        _MappedEvents[T, R](this, f)
                            ^

I thought it was significant that by changing the type param names, a different error message resulted. This has made me wonder if perhaps there may be confusion in the compiler regarding type parameter names in subtyping hierarchies? Again this is just speculation with limited knowledge and understanding of how things actually work. FWIW, I seem to remember solving problems I've had in the past by swapping the common type param name T for something else.

The next thing I did was change the Events definition from a trait to an interface, and looked at the resulting error messages using both versions of _MappedEvents. My assumption was that the use of either a trait or interface would be equivalent in this case, and that that the error messages would be the same in both cases of _MappedEvents, but this is not the case. Defining Events as an interface and using the version of _MappedEvents with unique type param names results in a different error message, which is more a combination of the previous two (as shown above) along with issues of 'incompatible type signature' on methods of the interface:

Error:
main.pony:14:25: argument not a subtype of parameter
    _MappedEvents[T, R](this, f)
                        ^
    Info:
    main.pony:14:25: argument type is _MappedEvents[A #share, B #share] ref
        _MappedEvents[T, R](this, f)
                            ^
    main.pony:44:5: parameter type is Events[R #share] ref
        from: Events[A],
        ^
    main.pony:10:18: Any #share is not a subtype of B #share: the type parameter has no lower bounds
      fun ref map[R: Any #share](
                     ^
    main.pony:50:3: parameter B #share is not a supertype of R #share
      fun ref set_event(e: B) => None
      ^
    main.pony:50:3: _MappedEvents[A #share, B #share] ref is not a subtype of Events[R #share] ref: method 'set_event' has an incompatible signature
      fun ref set_event(e: B) => None
      ^
    main.pony:38:39: Any #share is not a subtype of R #share: the type parameter has no lower bounds
    class _MappedEvents[A: Any #share, B: Any #share] is Events[B]
                                          ^
    main.pony:51:3: method result B #share is not a subtype of R #share
      fun ref get_event(): B =>
      ^
    main.pony:51:3: _MappedEvents[A #share, B #share] ref is not a subtype of Events[R #share] ref: method 'get_event' has an incompatible signature
      fun ref get_event(): B =>
      ^
    main.pony:38:39: Any #share is not a subtype of R #share: the type parameter has no lower bounds
    class _MappedEvents[A: Any #share, B: Any #share] is Events[B]
                                          ^
    main.pony:11:8: parameter R #share is not a supertype of B #share
        f: {(T): R}) // A lambda which transforms T to R
           ^
    main.pony:11:8: {(T): R}[R #share, R #share] ref is not a subtype of {(T): R}[B #share, R #share] ref: method 'apply' has an incompatible signature
        f: {(T): R}) // A lambda which transforms T to R
           ^
    main.pony:10:3: parameter {(T): R}[B #share, R #share] ref is not a supertype of {(T): R}[R #share, R #share] ref
      fun ref map[R: Any #share](
      ^
    main.pony:10:3: _MappedEvents[A #share, B #share] ref is not a subtype of Events[R #share] ref: method 'map' has an incompatible signature
      fun ref map[R: Any #share](
      ^

I'd be happy to hear that my intentions as laid out by the example are not unwarranted. I should also note that I've also tried replacing the object literal / lambdas with classes and found no difference to the above.

Thanks for taking a look!

@patternspandemic
Copy link
Contributor Author

Related: #1875, #1921
Possible fix: #1888

@patternspandemic
Copy link
Contributor Author

As suggested by @mfelsche, I applied the same kind of workaround as applied by @Theodus in ponylang/ponycheck#15 to the example above (with success) as seen in this playground. I think this indicates the underlying problem is the same, and hopefully is resolved by #1888.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant