You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
traitEvents[T: Any #share]
""" Assume `Events` represents a stream of events of type `T`. """// For simplificationfunrefset_event(e: T)
funrefget_event(): T// Maps this event stream of type `T` to another stream of type `R`funrefmap[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}
newcreate(
from: Events[T],
f: {(T): R})
=>
_from = from
_f = f
funrefset_event(e: R) => Nonefunrefget_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)*/actorMainnewcreate(env: Env) =>
// Construct an event stream of USizelet usize_events: Events[USize] =
objectref is Events[USize]
var _event: USize = 0funrefset_event(n: USize) => _event = n
funrefget_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 Stringlet 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!
The text was updated successfully, but these errors were encountered:
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.
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 typeString
.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
orString
(See also #2867). I do not understand the generic compilation process enough to know whether this is the case: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: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. DefiningEvents
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: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!
The text was updated successfully, but these errors were encountered: