Re: bug?? extend Object compilation order dependency?
I may have misunderstood what I've read about protocols, so please set me straight if the following is wrong - On Aug 25, 11:08 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: I think the current behavior follows the principle of least surprise: (1) bar is a function, in whatever namespace the protocol Foo is defined in But bar is not a single function. Multiple bars can coexist in some sense, right? Otherwise there's no polymorphism? (2) you redefine bar (perhaps by reloading the file Foo is in) I don't see this as bar being re-defined, rather bar is defined for fooed, then defined for Object (3) you call bar and get the new behavior It seems that the dispatch mechanism for bar is preferring Object's bar rather than user.fooed's bar. This is not what I expected. So maybe the right way to think about this is that protocols don't pay attention to class hierarchy when dispatching: user.fooed is an Object, and bar was defined for Object after bar was defined for user.fooed, so Object's bar is selected? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
I ran your sequence without redefining Foo and got 20 instead of 1/5. I don't know how that redefinition affects the evaluation of bar. Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 On Aug 25, 8:55 am, DeverLite derby.lit...@gmail.com wrote: So if I do this in a clean REPL Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (defprotocol Foo (bar [this x])) Foo user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 1/5 user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 I would have expected 20 the first call to bar to be 20. This is a natural sequence of lines to execute if you---for instance--- have Foo defined in file A and fooed in file B, and you then add a default implementation using extend-type in file A and then recompile file A. This scenario is easily fixed by recompiling file B (as the last few lines demonstrate). I don't see any easy way to actually fix this behavior (though my understanding of how protocols are implemented is largely based on seeing how they behave and my limited knowledge of the JVM, rather than actually looking at the code), but it does seem like it would be less surprising if it could be accomplished. Thoughts? Should I post this as a ticket in assembla? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
Perhaps I should have explained why I think 20 should be the right answer. This is based on my expectation that once I've compiled a file the behavior of records or types defined in that file will not change (unless I re-bind functions called by the record or type). The above behavior violates this principle in a very natural use case where I add or recompile a default implementation of a protocol located in a separate file from a record implementing that protocol. On a more practical level the current behavior means I have to explicitly recompile multiple files when I change a line in one file, which is somewhat inconvenient (perhaps there should be a way to auto- detect this situation and have the Clojure compiler recompile the appropriate file???). If I forget to do this then the code will behave in unpredictable ways because I've suddenly replaced the non-default implementations with a new default implementation. On Aug 26, 7:08 am, MikeM michael.messini...@invista.com wrote: I may have misunderstood what I've read about protocols, so please set me straight if the following is wrong - On Aug 25, 11:08 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: I think the current behavior follows the principle of least surprise: (1) bar is a function, in whatever namespace the protocol Foo is defined in But bar is not a single function. Multiple bars can coexist in some sense, right? Otherwise there's no polymorphism? (2) you redefine bar (perhaps by reloading the file Foo is in) I don't see this as bar being re-defined, rather bar is defined for fooed, then defined for Object (3) you call bar and get the new behavior It seems that the dispatch mechanism for bar is preferring Object's bar rather than user.fooed's bar. This is not what I expected. So maybe the right way to think about this is that protocols don't pay attention to class hierarchy when dispatching: user.fooed is an Object, and bar was defined for Object after bar was defined for user.fooed, so Object's bar is selected? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
My understanding is that old (previously constructed) instances of a record follow the old implementation. To get the new behavior after recompiling a record you must construct a new instance. On Aug 26, 10:51 am, Armando Blancas armando_blan...@yahoo.com wrote: I ran your sequence without redefining Foo and got 20 instead of 1/5. I don't know how that redefinition affects the evaluation of bar. Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 On Aug 25, 8:55 am, DeverLite derby.lit...@gmail.com wrote: So if I do this in a clean REPL Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (defprotocol Foo (bar [this x])) Foo user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 1/5 user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 I would have expected 20 the first call to bar to be 20. This is a natural sequence of lines to execute if you---for instance--- have Foo defined in file A and fooed in file B, and you then add a default implementation using extend-type in file A and then recompile file A. This scenario is easily fixed by recompiling file B (as the last few lines demonstrate). I don't see any easy way to actually fix this behavior (though my understanding of how protocols are implemented is largely based on seeing how they behave and my limited knowledge of the JVM, rather than actually looking at the code), but it does seem like it would be less surprising if it could be accomplished. Thoughts? Should I post this as a ticket in assembla? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
Someone correct me if I'm wrong, but I think what's happening is that when you call (defprotocol Foo ...) for the second time, you're setting the symbol Foo to point to the new protocol, while the original fooed still has the old protocol. It also binds bar to a completely new function, which has only been set to work with the new protocol so far. So the old bar, if it still existed, would work fine with the old fooed, but the new bar has nothing set for dealing with the old protocol. I think that if you took out the second defprotocol it would work fine. On Aug 25, 11:55 am, DeverLite derby.lit...@gmail.com wrote: So if I do this in a clean REPL Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (defprotocol Foo (bar [this x])) Foo user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 1/5 user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 I would have expected 20 the first call to bar to be 20. This is a natural sequence of lines to execute if you---for instance--- have Foo defined in file A and fooed in file B, and you then add a default implementation using extend-type in file A and then recompile file A. This scenario is easily fixed by recompiling file B (as the last few lines demonstrate). I don't see any easy way to actually fix this behavior (though my understanding of how protocols are implemented is largely based on seeing how they behave and my limited knowledge of the JVM, rather than actually looking at the code), but it does seem like it would be less surprising if it could be accomplished. Thoughts? Should I post this as a ticket in assembla? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
I may have misunderstood what I've read about protocols, so please set me straight if the following is wrong - On Aug 25, 11:08 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: I think the current behavior follows the principle of least surprise: (1) bar is a function, in whatever namespace the protocol Foo is defined in But bar is not a single function. Multiple bars can coexist in some sense, right? Otherwise there's no polymorphism? No, bar is a single function, with many specialized implementations. (2) you redefine bar (perhaps by reloading the file Foo is in) I don't see this as bar being re-defined, rather bar is defined for fooed, then defined for Object The original example didn't just define bar, it also redefined the protocol itself, which is the source of the confusion. If it had only defined the protocol. then extended it to various things, your expectation would be correct. (3) you call bar and get the new behavior It seems that the dispatch mechanism for bar is preferring Object's bar rather than user.fooed's bar. This is not what I expected. No. The dispatch mechanism for bar has been discarded and replaced by a new one. -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
The problem here is that you are presuming that a protocol function bar is a behavior of the types it is extended to. Invert that: bar is its own function, and the types come to it, not the other way around. If you explicitly replace bar with a new function (which redeclaring the protocol does), then call the new function, you will get ... the new function. Types don't have behaviors. Behaviors have (a dynamic association with) types. The fact that you can declare the behavior inline on a type is a convenience: The controlling entity is the function, not the type. There is a real practical issue here, but it is more modest than what you describe. You can add a new default implementation anytime you want, without causing any problems with existing implementations. It is redefining the *protocol* that causes a need to revisit other code. Tools could indeed help with this. I realize that this is unfamiliar for people (including myself) coming from OO, but I want to emphasize that your confusion is not about an ancillary point -- it is central to how protocols work. Hope this helps, Stu Stuart Halloway Clojure/core http://clojure.com Perhaps I should have explained why I think 20 should be the right answer. This is based on my expectation that once I've compiled a file the behavior of records or types defined in that file will not change (unless I re-bind functions called by the record or type). The above behavior violates this principle in a very natural use case where I add or recompile a default implementation of a protocol located in a separate file from a record implementing that protocol. On a more practical level the current behavior means I have to explicitly recompile multiple files when I change a line in one file, which is somewhat inconvenient (perhaps there should be a way to auto- detect this situation and have the Clojure compiler recompile the appropriate file???). If I forget to do this then the code will behave in unpredictable ways because I've suddenly replaced the non-default implementations with a new default implementation. On Aug 26, 7:08 am, MikeM michael.messini...@invista.com wrote: I may have misunderstood what I've read about protocols, so please set me straight if the following is wrong - On Aug 25, 11:08 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: I think the current behavior follows the principle of least surprise: (1) bar is a function, in whatever namespace the protocol Foo is defined in But bar is not a single function. Multiple bars can coexist in some sense, right? Otherwise there's no polymorphism? (2) you redefine bar (perhaps by reloading the file Foo is in) I don't see this as bar being re-defined, rather bar is defined for fooed, then defined for Object (3) you call bar and get the new behavior It seems that the dispatch mechanism for bar is preferring Object's bar rather than user.fooed's bar. This is not what I expected. So maybe the right way to think about this is that protocols don't pay attention to class hierarchy when dispatching: user.fooed is an Object, and bar was defined for Object after bar was defined for user.fooed, so Object's bar is selected? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
Redefining Foo gives you a new implementation of bar, which, like any protocol method, will fail for all inputs until a type has been extended to it. I ran your sequence without redefining Foo and got 20 instead of 1/5. I don't know how that redefinition affects the evaluation of bar. Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 On Aug 25, 8:55 am, DeverLite derby.lit...@gmail.com wrote: So if I do this in a clean REPL Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (defprotocol Foo (bar [this x])) Foo user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 1/5 user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 I would have expected 20 the first call to bar to be 20. This is a natural sequence of lines to execute if you---for instance--- have Foo defined in file A and fooed in file B, and you then add a default implementation using extend-type in file A and then recompile file A. This scenario is easily fixed by recompiling file B (as the last few lines demonstrate). I don't see any easy way to actually fix this behavior (though my understanding of how protocols are implemented is largely based on seeing how they behave and my limited knowledge of the JVM, rather than actually looking at the code), but it does seem like it would be less surprising if it could be accomplished. Thoughts? Should I post this as a ticket in assembla? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
Thank you Stuart. All the years of thinking about functions as being subservient to objects has warped my brain. I believe I've read about this inversion for protocols before but I don't think I've really grasped all the consequences of it until now. What I'm taking away from this is that it is probably best practice to have protocols in a separate namespace from any implementations to avoid confusion when I recompile an implementation (file). On Aug 26, 12:36 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: The problem here is that you are presuming that a protocol function bar is a behavior of the types it is extended to. Invert that: bar is its own function, and the types come to it, not the other way around. If you explicitly replace bar with a new function (which redeclaring the protocol does), then call the new function, you will get ... the new function. Types don't have behaviors. Behaviors have (a dynamic association with) types. The fact that you can declare the behavior inline on a type is a convenience: The controlling entity is the function, not the type. There is a real practical issue here, but it is more modest than what you describe. You can add a new default implementation anytime you want, without causing any problems with existing implementations. It is redefining the *protocol* that causes a need to revisit other code. Tools could indeed help with this. I realize that this is unfamiliar for people (including myself) coming from OO, but I want to emphasize that your confusion is not about an ancillary point -- it is central to how protocols work. Hope this helps, Stu Stuart Halloway Clojure/corehttp://clojure.com Perhaps I should have explained why I think 20 should be the right answer. This is based on my expectation that once I've compiled a file the behavior of records or types defined in that file will not change (unless I re-bind functions called by the record or type). The above behavior violates this principle in a very natural use case where I add or recompile a default implementation of a protocol located in a separate file from a record implementing that protocol. On a more practical level the current behavior means I have to explicitly recompile multiple files when I change a line in one file, which is somewhat inconvenient (perhaps there should be a way to auto- detect this situation and have the Clojure compiler recompile the appropriate file???). If I forget to do this then the code will behave in unpredictable ways because I've suddenly replaced the non-default implementations with a new default implementation. On Aug 26, 7:08 am, MikeM michael.messini...@invista.com wrote: I may have misunderstood what I've read about protocols, so please set me straight if the following is wrong - On Aug 25, 11:08 pm, Stuart Halloway stuart.hallo...@gmail.com wrote: I think the current behavior follows the principle of least surprise: (1) bar is a function, in whatever namespace the protocol Foo is defined in But bar is not a single function. Multiple bars can coexist in some sense, right? Otherwise there's no polymorphism? (2) you redefine bar (perhaps by reloading the file Foo is in) I don't see this as bar being re-defined, rather bar is defined for fooed, then defined for Object (3) you call bar and get the new behavior It seems that the dispatch mechanism for bar is preferring Object's bar rather than user.fooed's bar. This is not what I expected. So maybe the right way to think about this is that protocols don't pay attention to class hierarchy when dispatching: user.fooed is an Object, and bar was defined for Object after bar was defined for user.fooed, so Object's bar is selected? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
bug?? extend Object compilation order dependency?
So if I do this in a clean REPL Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (defprotocol Foo (bar [this x])) Foo user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 1/5 user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 I would have expected 20 the first call to bar to be 20. This is a natural sequence of lines to execute if you---for instance--- have Foo defined in file A and fooed in file B, and you then add a default implementation using extend-type in file A and then recompile file A. This scenario is easily fixed by recompiling file B (as the last few lines demonstrate). I don't see any easy way to actually fix this behavior (though my understanding of how protocols are implemented is largely based on seeing how they behave and my limited knowledge of the JVM, rather than actually looking at the code), but it does seem like it would be less surprising if it could be accomplished. Thoughts? Should I post this as a ticket in assembla? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en
Re: bug?? extend Object compilation order dependency?
I think the current behavior follows the principle of least surprise: (1) bar is a function, in whatever namespace the protocol Foo is defined in (2) you redefine bar (perhaps by reloading the file Foo is in) (3) you call bar and get the new behavior Remember that bar lives in Foo's namespace, not in fooed's. Also: what should your expectation be if the second definition of Foo had changed the arity of bar? Stu So if I do this in a clean REPL Clojure 1.2.0 user= (defprotocol Foo (bar [this x])) Foo user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (defprotocol Foo (bar [this x])) Foo user= (extend-type Object Foo (bar [this x] (/ 2 x))) nil user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 1/5 user= (defrecord fooed [] Foo (bar [this x] (* 2 x))) user.fooed user= (def fooey (fooed.)) #'user/fooey user= (bar fooey 10) 20 I would have expected 20 the first call to bar to be 20. This is a natural sequence of lines to execute if you---for instance--- have Foo defined in file A and fooed in file B, and you then add a default implementation using extend-type in file A and then recompile file A. This scenario is easily fixed by recompiling file B (as the last few lines demonstrate). I don't see any easy way to actually fix this behavior (though my understanding of how protocols are implemented is largely based on seeing how they behave and my limited knowledge of the JVM, rather than actually looking at the code), but it does seem like it would be less surprising if it could be accomplished. Thoughts? Should I post this as a ticket in assembla? -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en -- You received this message because you are subscribed to the Google Groups Clojure group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en