Spin2

David BetzDavid Betz Posts: 14,179
edited 2019-12-26 - 23:40:24 in Propeller 2
@cgracy Do you have a language definition for Spin2? I realize that you haven't finished it yet but I believe you said in your video that it was nearly complete so I'm assuming that the language design itself is mostly frozen. What I'm wondering is if you have provided a way to pass a reference to an object as a parameter to a method. That was the biggest gap in the original Spin as far as I'm concerned and I'm wondering if you've addressed that in Spin2.
«13456718

Comments

  • I'm not quite there, yet.

    We have function pointers, anyway, and you can make a pointer from any method. It takes two longs. Once it's set up, you can pass its address around and any method can use it. You'd just better be sure your numbers of parameters and return values are in agreement.
  • very cool @cgracey

    sure one has to take care of parameters/returns, but this sounds extremely nice.

    I poked you a little bit at the webinar with cooperating and looking at @ersmith extensions. I do understand your point of view that you need to build/think thru your own vision.

    Actually I poked you to get Spin2 at least out to test it. It will not be nailed in stone anyways, there will be tons of versions of the interpreter, because you have to include the interpreter into your binary anyways.

    One of the Dave's and David's which I always mix up had the brilliant Idea that a smart compiler could - theoretically - just include the used bytecodes and code to save COG mem for user defined functions.

    Anyways, being able to call HUBexec PASM code is one step, how about the other way around?

    Do you have any idea how to 'callback' from PASM to SPIN? Can I call a function Pointer from PASM back to SPIN, assuming I am running code in/with the interpreter COG?

    curious,

    Mike
  • msrobots wrote: »
    One of the Dave's and David's which I always mix up had the brilliant Idea that a smart compiler could - theoretically - just include the used bytecodes and code to save COG mem for user defined functions.
    I think that was likely Eric Smith's idea. In any case, it wasn't mine although it does seem like an interesting approach now that the byte code interpreter isn't in ROM.

  • cgraceycgracey Posts: 13,078
    edited 2019-12-27 - 17:19:20
    It would require setting up a stack context from which the spin code could work. It's totally doable, and not that complex, but Spin code is contextual in memory (think VAR) and unless you can borrow that context from the Spin code base, it might be a little crazy to try to set up from PASM.

    It would be a lot simpler to make your main context Spin, then call to PASM code which returns values which Spin makes decisions from.
  • So I guess no object references for Spin2?
  • I see Eric's FastSpin supports the following capability. This is satisfies my request for object references. Is Spin2 going to support this as well?
    Abstract objects and object pointers
    
    The proposed Spin2 syntax for abstract object definitions and object pointers is accepted. A declaration like:
    
    OBJ
      fds = "FullDuplexSerial"
    declares fds as having the methods of a FullDuplexSerial object, but without any actual variable or storage being instantiated. Symbols declared this way may be used to cast parameters to an object type, for example:
    
    PUB print(f, c)
      fds[f].dec(c)
    
    PUB doprint22
      print(@aFullDuplexSerialObj, 22)
    
  • That's a more strict way to achieve the same thing. I might make it more like that. I need to get it working, first.
  • cgracey wrote: »
    That's a more strict way to achieve the same thing. I might make it more like that. I need to get it working, first.
    What is your way of doing this?

  • cgraceycgracey Posts: 13,078
    edited 2019-12-28 - 03:54:50
    David Betz wrote: »
    cgracey wrote: »
    That's a more strict way to achieve the same thing. I might make it more like that. I need to get it working, first.
    What is your way of doing this?

    To make a method pointer:

    MPTR[address_of_pointer_to_assign] := {object_name{[index]}.}method_name{[index]}

    To use a method pointer:

    ~~method_pointer{:number_of_return_values_if_not_zero}{[index]}{(parameter1{,parameter2...})}

    The method pointer stores the entire context (including pbase, vbase, and method index).

    I remember all the talk about blank objects, but if I'm thinking correctly, this simple mechanism will cover for everything. Do you see a shortcoming? One thing I see is that this is very generic and the compiler doesn't know or care about numbers of parameters or their names. It's all a run-time issue.
  • I do not really get Davids example, but I do like the 2 long method pointer.

    As to expect Eric does it the formalized(?) way, type checking and such, and Chip does give a whatever about conventions, and it might be good so.

    The freedom to shoot yourself in the foot if your types/parameters don't match allows also to USE this side effect for interesting things.

    Spin2 will be very interesting.

    Mike
  • tastes do differ ;)
    I find eric's example very easy to read, understand and use. It hides the object structure details.

    chip's proposal assumes you know precisely what you're doing and understand the object structure details. More complicated in my eyes
  • msrobots wrote: »
    I do not really get Davids example, but I do like the 2 long method pointer.

    As to expect Eric does it the formalized(?) way, type checking and such, and Chip does give a whatever about conventions, and it might be good so.

    The freedom to shoot yourself in the foot if your types/parameters don't match allows also to USE this side effect for interesting things.

    Spin2 will be very interesting.

    Mike
    It isn't *my* example. I took that description directly out of the FastSpin documentation. Presumably it is already implemented and working in FastSpin. In addition, the wording makes it sound like it was something agreed upon among forum members possibly including Chip some time ago.

    Here is an idea. Maybe Parallax should contract with Eric to implement a byte code backend for his FastSpin suite of languages. The actual definition of the byte codes could be done by Chip and Eric together and the implementation of the byte code interpreter could be done by Chip. The result would be a cross platform implementation of Spin, BASIC, and C for both the P1 and the P2 that can support either native code generation for speed or byte code generation for space efficiency. Since the front end of Spin, BASIC, and C are already done and working this should be a fairly quick process. The alternative is to end up with a Spin2 that is actually less capable than FastSpin and only runs under Windows.

  • cgraceycgracey Posts: 13,078
    edited 2019-12-28 - 12:12:35
    Let's say you want to give an object a pointer to a method that outputs characters. At the time the object was written, this method didn't even exist. The object only needs to know that the method takes one parameter, which the object will supply when it calls the method indirectly through a pointer. For this to be flexible, all the details can't be known about the method, only that it takes some number of parameters and returns some number of results - that's it, right? I'm not understanding/remembering the purpose of declaring something as an exact type of object. It seems to me that some simple agreement of #params and #results is all that matters.
  • cgracey wrote: »
    Let's say you want to give an object a pointer to a method that outputs characters. At the time the object was written, this method didn't even exist. The object only needs to know that the method takes one parameter, which the object will supply when it calls the method indirectly through a pointer. For this to be flexible, all the details can't be known about the method, only that it takes some number of parameters and returns some number of results - that's it, right? I'm not understanding/remembering the purpose of declaring something as an exact type of object. It seems to me that some simple agreement of #params and #results is all that matters.
    I think there could easily be a case where you want to pass a reference to an object not just a method of an object. What if you need to call more than one method? I guess you could pass multiple method pointers but that would be redundant since each method pointer would include a pointer to the object data. Also, it's cleaner to just pass a pointer to the object itself. Anyway, Eric has this all worked out. Why not take advantage of his work and collaborate on a byte code solution?

  • cgracey,
    Let's say you want to give an object a pointer to a method that outputs characters.... I'm not understanding/remembering the purpose of the formality of saying something is an exact type of object. It seems to me that some simple agreement of #params and #results is all that matters.
    All that formality comes from the world of Object Oriented Programming. Especially C++.

    In OOP if you want to call a method on an object that object has to come from some class. A type. The caller has to have a pointer or reference to that type. The caller cannot be written before it has a definition of the type it is calling the method on. (Give or take some further messing with inheritance and so on)

    Of course as you have noticed none of the particular formality is needed:
    The object[caller] only needs to know that the method takes one parameter, which the object will supply when it calls the method indirectly through a pointer. For this to be flexible, all the details can't be known about the method, only that it takes some number of parameters and returns some number of results - that's it, right?
    I believe that is right.

    In my current favorite language, Rust, there are no classes, no inheritance etc. Rather it is built around 'traits'.

    With traits one can define methods that can be used by some other objects, which may not even be written yet.

    For example: One can write a trait without knowing anything about any objects like so:
    trait HasArea {
        fn area(&self) -> f64;
    }
    
    The someone else can create an object:
    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }
    
    impl HasArea for Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }
    
    All of which sounds more like what you are thinking about.




  • Heater. wrote: »
    cgracey,
    Let's say you want to give an object a pointer to a method that outputs characters.... I'm not understanding/remembering the purpose of the formality of saying something is an exact type of object. It seems to me that some simple agreement of #params and #results is all that matters.
    All that formality comes from the world of Object Oriented Programming. Especially C++.

    In OOP if you want to call a method on an object that object has to come from some class. A type. The caller has to have a pointer or reference to that type. The caller cannot be written before it has a definition of the type it is calling the method on. (Give or take some further messing with inheritance and so on)

    Of course as you have noticed none of the particular formality is needed:
    The object[caller] only needs to know that the method takes one parameter, which the object will supply when it calls the method indirectly through a pointer. For this to be flexible, all the details can't be known about the method, only that it takes some number of parameters and returns some number of results - that's it, right?
    I believe that is right.

    In my current favorite language, Rust, there are no classes, no inheritance etc. Rather it is built around 'traits'.

    With traits one can define methods that can be used by some other objects, which may not even be written yet.

    For example: One can write a trait without knowing anything about any objects like so:
    trait HasArea {
        fn area(&self) -> f64;
    }
    
    The someone else can create an object:
    struct Circle {
        x: f64,
        y: f64,
        radius: f64,
    }
    
    impl HasArea for Circle {
        fn area(&self) -> f64 {
            std::f64::consts::PI * (self.radius * self.radius)
        }
    }
    
    All of which sounds more like what you are thinking about.



    But even with that scheme you still end up passing a reference to a Circle to a function not a reference to a method of Circle, right?

  • cgraceycgracey Posts: 13,078
    edited 2019-12-28 - 12:40:41
    David Betz wrote: »
    cgracey wrote: »
    Let's say you want to give an object a pointer to a method that outputs characters. At the time the object was written, this method didn't even exist. The object only needs to know that the method takes one parameter, which the object will supply when it calls the method indirectly through a pointer. For this to be flexible, all the details can't be known about the method, only that it takes some number of parameters and returns some number of results - that's it, right? I'm not understanding/remembering the purpose of declaring something as an exact type of object. It seems to me that some simple agreement of #params and #results is all that matters.
    I think there could easily be a case where you want to pass a reference to an object not just a method of an object. What if you need to call more than one method? I guess you could pass multiple method pointers but that would be redundant since each method pointer would include a pointer to the object data. Also, it's cleaner to just pass a pointer to the object itself. Anyway, Eric has this all worked out. Why not take advantage of his work and collaborate on a byte code solution?

    You could get a pointer to a base method and then [index] the particular method.

    I'm just not remembering why you'd need to know everything exactly beforehand. That's like making everything NOT flexible.

    I don't know where I'm going, exactly, but I do know how to find my way there. It's too early to collaborate, yet.
  • Hmm may be..

    Thing is in the procedural C world we have structs and functions and you end up writing:

    a = area(&someCircle);

    (Assuming you need more than one circle you need to specify which one with that parameter.

    In the C++ world we end up writing:

    a = someCircle.area();

    Which is perhaps syntactically nicer. The messy instance pointer is hidden. Here C++ knows which circle we mean because someCircle is an instance of class Circle.

    With Rust traits you end up being able to write the same. But you can also write:

    a = area(&someCircle)

    The OOP style syntax is just syntactic sugar.

    I'm not sure how this all applies to Spin. If I recall correctly Spin objects were not classes. One could only have one instance of an object. Do I recall correctly or has that changed for Spin 2?











  • Spin objects are infact classes, you can instantiate an object as many times as you want and it only allocates more VAR space. (You can even have arrays of objects, which is a bit of an obscure feature) The limitation is that you can only allocate them at compile time.
  • cgraceycgracey Posts: 13,078
    edited 2019-12-28 - 13:22:52
    Wuerfel_21 wrote: »
    Spin objects are infact classes, you can instantiate an object as many times as you want and it only allocates more VAR space. (You can even have arrays of objects, which is a bit of an obscure feature) The limitation is that you can only allocate them at compile time.

    Okay. This is ringing a bell. I think that's why we hashed out the phantom object declarations a while back. It would allow us to reuse VAR space for objects whose code is present, but they are not given VAR space, yet.
  • cgracey wrote: »
    You could get a pointer to a base method and then [index] the particular method.
    But then you need to know the offsets of all of the methods you want to call.

  • cgraceycgracey Posts: 13,078
    edited 2019-12-28 - 13:23:35
    David Betz wrote: »
    cgracey wrote: »
    You could get a pointer to a base method and then [index] the particular method.
    But then you need to know the offsets of all of the methods you want to call.

    Yes, but if the point is to make things interchangeable, you would have the same types of routines at each unique index for many different objects.
  • cgracey wrote: »
    David Betz wrote: »
    cgracey wrote: »
    You could get a pointer to a base method and then [index] the particular method.
    But then you need to know the offsets of all of the methods you want to call.

    Yes, but if the point is to make things interchangeable, you would have the same types of routines at each unique index for many different objects.
    But you have to do that by convention and keep track of it manually. If you use object references those details are handled by the compiler. One of the strengths of Spin is that everything is compiled together so you can know everything at compile time. You don't have to worry about linking separately compiled object files like in C/C++.

  • Pointers to objects and pointers to methods are related, but different, and they're both useful in different circumstances. There are definitely times when you just want to call a function (e.g. to output a character) and then a simple method pointer will do the trick. But there are also times when there is a collection of data and functions that you want to use together (an object!) and you want to pass a pointer to that. It'd be handy to have both, as most object oriented languages do (including fastspin's BASIC and C).
  • ersmith wrote: »
    Pointers to objects and pointers to methods are related, but different, and they're both useful in different circumstances. There are definitely times when you just want to call a function (e.g. to output a character) and then a simple method pointer will do the trick. But there are also times when there is a collection of data and functions that you want to use together (an object!) and you want to pass a pointer to that. It'd be handy to have both, as most object oriented languages do (including fastspin's BASIC and C).
    As do FastSpin's BASIC and C? Does that mean FastSpin itself doesn't have both?

  • potatoheadpotatohead Posts: 10,117
    edited 2019-12-28 - 16:40:43
    The freedom to shoot yourself in the foot if your types/parameters don't match allows also to USE this side effect for interesting things.

    This is how I feel about it too. Other aspects of Spin are loose, Chip calls it "Wild Programming." Seems these ideas are in line with that overall feel.

    There is nothing here that cannot be undone or changed. ---except for that which is not yet done.

    I saw someone put fresh ordinary drip coffee grounds onto vanilla ice cream. I know, right? My response too. WTH?

    But, then I had a scoop. Guys, coffee is the pepper of ice cream. It's insane good. Aromatics, the texture of the coffee bits, creamy vanilla... You should try it. I love it personally.

    Who knew?


    Let's see the chef prepare the dish. Then we can talk about the taste of the food.



  • potatohead wrote: »
    Let's see the chef prepare the dish. Then we can talk about the taste of the food.
    That sounds good in theory except that a lot of work goes into crafting a language definition and compiler and so going down the wrong path could mean a lot of work being undone. Chip took input from everyone on the design of the P2 chip itself. Why not Spin2 as well?

  • potatoheadpotatohead Posts: 10,117
    edited 2019-12-28 - 17:28:56
    Going down a different path could result in those things too. What's worth what?

    Nobody can know at this point. Wrong isn't even in the discussion yet.

    Also, there are different perspectives in play here. Chip is thinking in terms of something that will compile on P2, rapidly. And that in some potential environment that works very differently from the usual build chain on a PC.

    That's a very different take on things. Understanding what it's gonna mean pretty much means seeing an expression of it.

    I'm pretty sure that overall idea governs things like boiling this down to the nubs, as was just done. Needs to be simple, fast, flexible.
  • Input to P2 had the effect to expand the possibilities of P2. "Wild" programming also expands possibilities, but at the risk running into the wild. So we have to take care to educate US to make good use of our freedom. That obviously it the hardest part of all. If we are not educated, no checks and balances can save us.
  • David Betz wrote: »
    ersmith wrote: »
    Pointers to objects and pointers to methods are related, but different, and they're both useful in different circumstances. There are definitely times when you just want to call a function (e.g. to output a character) and then a simple method pointer will do the trick. But there are also times when there is a collection of data and functions that you want to use together (an object!) and you want to pass a pointer to that. It'd be handy to have both, as most object oriented languages do (including fastspin's BASIC and C).
    As do FastSpin's BASIC and C? Does that mean FastSpin itself doesn't have both?

    The fastspin Spin dialect does have both, but while the object pointers are documented (as you've seen) method pointers are not. The syntax for method pointers in fastspin seems to be different from what Chip chose, so probably it isn't worth documenting until Chip's final Spin2 specification is released.

    For BASIC and C both function and object pointers work pretty much the way you would expect. "@obj.method" (or "&obj.method" in C) creates what's essentially a closure which will call the given method for the given (particular) object. Under the hood this is represented as a single 32 bit pointer to a pair of longs, one of which is a pointer to the object's data and one of which is a pointer to the function itself.
Sign In or Register to comment.