-
Notifications
You must be signed in to change notification settings - Fork 7
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
Solution to the Expression Problem #15
Comments
---------------------------- Original Message ---------------------------- Hi Andrey, This is Shelby Moore III from the discussion at the following link: When you read the following which is an internal note for my Copute The idea is that mixin methods are functions that input this as first
But that doesn't provide to the caller the inversion-of-control, i.e. the The solution is for each function to specify the interface it requires of
The solution lies in the vtable and the mixin. Each object has a pointer Thus to convert an object to an extended interface, specify which mixin(s) To view an object as having an extended interface, there is no need to I have no idea if this can be done efficiently on the current version of So the syntax should be as follows (not supported by Scala), where val x : Extended = non_extended : NonExtended, Extensions,... // In Scala The caller can convert before sending the input, which means the function We should over time make our compiler smart enough to automatically |
---------------------------- Original Message ---------------------------- Hi Shelby,
I have some questions here:
I am aware of no way of using a vtable at compile time.
The same problems here: identity will be lost, and it is unclear what to do with those methods of the target interface that were present in the original object. Plus it will emit very many classes.
This algorithm should be pretty clever. I would be interested in how you can do that efficiently. Thanks for your time. Andrey Breslav |
---------------------------- Original Message ----------------------------
The original object does not change. The idea is to copy the object and change the vtable in the copy to the I understand that conceptually, an object is a structure of data fields Conceptually, an object could be two pointers, one to its data, and And this is only for conversions. I also wrote below about compile-time I briefly looked at the JVM spec and I don't think it supports this direct
The programmer is specifying this, just as they do when they declare a This is all resolved at compile-time, so it isn't slowed at all afaics. In the following, the vtable for "Extended = NonExtended, Extensions,..." val x : Extended = non_extended : NonExtended, Extensions,... It is analogous to declaring a mixin, where NonExtended is now a mixin mixin Extended inherits NonExtended, Extensions,...
When we define a class at compile-time, we are setting the vtable for all Apparently JVM does not give us the control to say "view this object's But I am not interested in the past, I am interested in the future, and I think going down the road of past spaghetti (i.e. not generalized to
I am not clear what you mean. Do you mean the original object will be
This depends on the language's specification. There is no problem here
That is a good thing. Absolutely necessary for wide-scale compositionality. But this can be anonymous classes (at least at the layer of our language, val x = non_extended : NonExtended, Extensions,...
This isn't central to the idea. Just a note I added to myself for an area The more key thing I want to present is that only this mixin + vtable
Ditto. Interested to hear any feedback, if you have.
|
---------------------------- Original Message ---------------------------- Hi Shelby,
Your idea about switching vtables at runtime is nice, indeed. It lies very close to metaclasses (as in Smalltalk). Another problem would be: how do the method implementations available in mixins know what data is available in the object they are attached to and how it is laid out in memory. I mean, a single mixin may be extending many objects with different sets of fields, and how to access those fields is unclear from the perspective of the mixin code. I guess, you need some static declarations of fields (or like) on the mixin side, as well as something to resolve memory layout problems on the runtime side.
I might not have got you right. I thought you are proposing implicit conversion to what is required at a call site. In this case the user if left with an impression of using the same object, but s/he actually isn't, because the function will receive an extension instead. If what you mean is explicit conversion, then this is sort of done conventionally by an Adapter or Decorator patterns. These patterns which suffer from some limitations, which your approach wouldn't have, but speaking of scalability, it's not a huge difference.
Well, on the JVM multiple inheritance of mixins is a difficult enough business and it introduces significant overhead, unless you do exactly as much as the aforementioned Adapter and Decorator patterns.
Kills performance. No kidding, too many classes on the JVM is a real risk of running out of memory (special kind of it, called PermGenSpace).
You are right about Scala's implicits, but what I wonder about is what exactly do you mean by "compositionality". They usually use the term in the context of semantics and various calculi, none of which appears in this discussion. Andrey Breslav |
---------------------------- Original Message ----------------------------
Thanks very much for that VM insight. On JVM, to transparently (i.e. low level detail not seen in language I got the idea by comparing what you want to do and what Scala's implicits
In Copute, there are no fields. Only classes may have constructor So I was only talking about extending an interface (because in Copute only It was one of the key insights I had to separate implementation from Perhaps it helps that Copute is an immutable language, meaning the I need to think more about the optimum low-level VM layout of the class
The fundamental concept is that the caller is providing the pointer to set It should be feasible with a sufficiently tuned VM to replace the We may think when writing a function that for example "swap" should only Afaics, the Adapter pattern is essentially what implicits do in this case,
I haven't studied this. I haven't studied how Scala performs in this regard. If so, this will need to improve, if I am correct that mixin scalability
I don't know what is the technical reason for a tradeoff on performance On the surface this sounds like a low-level optimization issue. I read that the PermGenSpace can be resized at runtime. You definitely raise the issue of why the constructor parameters (data) I want to assume my idea won't be any worse performing than Scala's
I've seen the term used when discussing operational semantics. Here I am Apologies my reply is less concrete than I want.
|
---------------------------- Original Message ---------------------------- Hi Shelby,
So, in the vtable for an extended object you'd have the methods of the original object that would access the data, and all the other methods would call those ones.
It is required, because if you allow mutable state, your classes would want some state that is not passed as a constructor argument at all, but is implemented internally (e.g. a cache or something like that).
You might be interested in Eiffel's approach to this problem. Bertrand Meyer is very proud of doing vtables better that C++.
There are quite a few immutable languages out there. ML dialects are very successful, especially OCaml and recently F#. Yes, they have mutability with references, but this is a pretty isolated feature. They say Fantom is successfully applied. Haskell is the most sophisticated and the only lazy language, and that's it. There are very many alternatives.
This sounds a lot like structural typing. You might want to look at the Go language, they have interfaces and classes similar to what you have and typing is structural. Andrey Breslav |
---------------------------- Original Message ---------------------------- Hi Andrey, Due to linearized multiple inheritance, the order of interfaces in the Thus, when referencing an interface that extends another interface, the It seems to me that each interface could have a consistent method order, Since member values (i.e. non-method data that change on each instance of So if we have a function that requires an Extended interface, and the A) Statically call extended methods, by passing the NonExtended interface B) Use some dynamic dispatch mechanism to apply an Extended interface to a These functions are not mixins, because a mixin which inherits from The choice for NonExtended may be arbitrary. To enable the most C) Caller could convert the NonExtended to an Extended. Same as for B, the Below the footnotes, I respond to your responses interleaved. [1] "Why not multiple inheritance?" in chapter on Traits in Programming in [2] http://www.research.ibm.com/people/d/dgrove/papers/oopsla01.pdf [3] Section "4.4 Multiple Inheritance in C++"[2], and A.C.Myers proposed
I described this in more detail in my latest reply above.
Static fields are an anti-pattern. If that is what you mean, I hope you http://lambda-the-ultimate.org/node/2678 Without static fields (i.e. global state), I don't think my idea is
There are only 3 I know of Haskell, Clean, and Mercury: http://james-iry.blogspot.com/2010/05/types-la-chart.html And none of those could ever go mainstream. They are too obtuse.
None of those are immutable (i.e. declarative), but rather imperative http://en.wikipedia.org/wiki/OCaml
Fantom's and Erlang's immutability is I think only optional, which defeats Fantom's type system has many corner cases, holes, and incomplete. Doesn't http://fantom.org/sidewalk/topic/675#c4549 The Actor model addresses concurrency, not parallelism: http://existentialtype.wordpress.com/2011/03/17/parallelism-is-not-concurrency/ Afaics, parallelism is much more important, because we are headed towards
It is not the most sophisticated in some key ways (although it may be the http://augustss.blogspot.com/2011/05/more-points-for-lazy-evaluation-in.html#4642367335333855323 http://existentialtype.wordpress.com/2011/04/24/the-real-point-of-laziness/ I studied Filinkski's paper, and summarized the conclusions at Copute's
There is nothing for the mainstream programmer at all. Big fat zero. I didn't want to create a language. But nobody will make the language we Scala is the best I've seen by far. But it is not enforcing immutability People are attracted to Clojure because it is simpler and more focused on Copute is basically Scala with a cleaned syntax and no mutability. And the
Afaik, structual typing doesn't have vtables known at compile-time, which
Structural typing is bad because it breaks behavioral covariance (Liskov I don't allow structural typing in Copute. Consider supplying an instance of List with count() member and Iterator The interface type implies a meaning. To structurally match on methods of I do think it would useful to support some kind of partial interface type. This would then have the same vtable format at compile-time as the full Actually this is a new idea, that you just caused me to articulate. I had |
---------------------------- Original Message ---------------------------- I decided to become opinionated, when Fantom developer argued that dynamic |
---------------------------- Original Message ---------------------------- Clarified the issue of extension: http://fantom.org/sidewalk/topic/675#c11399 |
---------------------------- Original Message ----------------------------
I don't mean static fields. I mean mutable instance fields, that have to internal to the class for it to maintain its invariants. Think of ArrayList that has two mutable fields: array reference and a number of cells used so far. Both must be maintained consistently, and not passed from the outside. As of immutable languages, have a closer look at Erlang. Andrey Breslav |
---------------------------- Original Message ----------------------------
The invariants are maintained because necessarily the extension methods I can see your concern, if this restriction was not enforced, i.e. if the
It is dynamically typed, and thus it is fail. Refer to the linked comments Also Erlang is geared for concurrency, and imo parallelism is more |
---------------------------- Original Message ----------------------------
Not only this. If all the fields are constructor parameters, this means that their initial values are passed in by an uncontrolled party, that does not guarantee any invariants. On the contrary, if there are fields that are not passed from the outside directly, the class can properly initialize them to guarantee the initial fulfillment of the invariant.
Good point. Andrey Breslav |
---------------------------- Original Message ----------------------------
I do not understand this. The constructor parameters always fulfill the Perhaps you are thinking that in some languages, the constructor
But I don't see how the constructor has anything to do with the "view Which is the also the case if we create an Extended instance, with a All I have done in the "view bound" case is take your idea for extension
|
---------------------------- Original Message ----------------------------
If only it could be so :) Let's look at ArrayList. It has two fields: size and array. So, in Copute, its constructor would have two parameters. Now, assume I call it this way: new ArrayList(10, new Object[5]) My invariants are totally broken at this point, although the types are respected. On the other hand, if I had a constructor with one parameter, size, and a non-parameter field, it would be fine, because the constructor could initialize the field in accordance with the invariant. Andrey Breslav |
---------------------------- Original Message ----------------------------
Or if you had dependent-typing, that the type of the second parameter Imo, the invariants are not broken, because the ArrayList has not declared The correct design for ArrayList is to not have a separate parameter for In any case, I don't see how this applies to the extension discussion. The |
---------------------------- Original Message ----------------------------
Well, you know, an ArrayList has to have a separate size field that is not equal (but not greater) than the array length. This is the only way to implement array expansion efficiently. And the statement you make here is very counter-practical. no type system will ever guarantee the invariants a programmer wants to guarantee. Even dependent types which, by the way, can not be fully powerful and sound at the same time if your language is Turing-complete.
We got pretty far in this discussion, and I'd like to remind you that we started from considering your approach to having only constructor parameters for the case of mutable fields. Andrey Breslav |
---------------------------- Original Message ----------------------------
And I have already explained how to do that in a operationally type safe As you note below, there is no way to make a language both turing complete Tangentially, this is really just Godel's theorem, which is another way of
The type system guarantees the invariants that it models, but of course The key is to design in a way that maximizes degrees-of-freedom, because So what I have shown is that my design does not limit the feature
I assume what you are thinking here is that constructor parameters are not It is possible there is some major hole in what I have been thinking Btw, I don't see this a competition as much as I want to collaborate. I Best,
|
---------------------------- Original Message ----------------------------
I missed this explanation, sorry.
Totally agreed. Andrey Breslav |
---------------------------- Original Message ---------------------------- Thanks also for the discussion. You helped to refine two important
|
---------------------------- Original Message ---------------------------- FYI, what we were talking about before. I am have very simple code I think I noticed an oversight in the paper about Scala's solution to the http://stackoverflow.com/questions/8409822/complete-solutions-to-the-expression-problem Afaics, the Scala solution does not address interoperability of the I think the paper made the wrong conclusion about multimethods, when they -Shelby |
The stackoverflow.com link in the prior email is dead (SO censored it!), so I saved a copy here: |
---------------------------- Original Message ---------------------------- I have added a "tradeoff rule" to my answer, and now I think it is correct http://stackoverflow.com/questions/8409822/complete-solutions-to-the-expression-problem You see the vtable swap that I was talking about, only applies in the -Shelby |
---------------------------- Original Message ---------------------------- Did you have a look at this paper? On Dec 8, 2011, at 12:17 , Shelby Moore wrote:
Andrey Breslav |
---------------------------- Original Message ---------------------------- It was that LtU thread that caused me to write that Q&A on SO, but I had Turns out that my tradeoff rule holds true, please see the comment I added Thanks for reminding me.
|
There is a summary in the 'Commentary' for
typeclass
, and the link to the full description.P.S. I found the early genesis for the idea for my solution to the Expression Problem. And the key link is the one from 2011 (which is dead, but this is the archive.org for it) when I was explaining to the creator of Kotlin that Extension Methods were insufficient and I started to explain my idea for a complete solution (but it wasn't fully fleshed out because I wasn't aware at that time that I was building on typeclasses).
What is really sad for me is on that day (May 1, 2012) that I invented and finally wrote down my solution to the Expression Problem, after writing that and uploading it, I laid down to sleep and within 1 hour I was in an ambulance rushed to ER for an acute peptic ulcer that had ruptured and was leaking acid into my abdomen. 4 years of hell since then... (note I was off and on sick before that, but not obliterated)
I will now go find my emails from 2011 with [email protected] and copy them into this thread, so there is a permanent public record.
The emails I think should make it clear I was thinking about conceptually how to change the implementation at the use-site function call.
Archive.org was returning the archived page, then it was redirecting after that. So I had to use web-sniffer.net to capture the page and then I uploaded it to my host.
The text was updated successfully, but these errors were encountered: