Shop Learn
New Spin - Page 21 — Parallax Forums

New Spin

1181921232436

Comments

  • Roy Eltham wrote: »
    jmg,
    I spent a fair amount of time trying to get @@@ working, and it's not possible with the current way code is compiled in OpenSpin(Chip's x86). It requires changing already compiled code after the fact (to fix up address values), and having full knowledge of all objects and their locations in memory after compiling and distilling. So it needs to modify the existing passes, and then add an additional pass to do the fix ups.

    So what you're saying is that it needs a linker. :)

    And regarding spaces / tabs / whitespace? It drives me nuts that Spin requires whitespace to delineate code blocks, so using that as an argument against tabs isn't going to sway me. I'd say it's better to decouple formatting from compilation. How many noobs on the forums post that they can't figure out why their code doesn't work because their indentation is all over the place?
  • cgraceycgracey Posts: 13,631
    edited 2017-02-28 01:06
    Jason and the rest of you, what do you think about this for allowing objects to access VARs from other instances of themselves?

    Top-level object instantiates three instances of "vals":
    OBJ
    
      v[3] : "vals"
    
    PUB DoIt : sum
    
      v[0].setxyz(1,2,3)    'assign by object index
      v[1].setxyz(4,5,6)
      v[2].setxyz(7,8,9)
    
      sum := v.sumxyz(0,1,2)    'sum of 1,5,9
    
    Note that "v." is the same as "v[0].".

    "vals" object:
    VAR
    
      long  x,y,z
    
    PUB setxyz(x_,y_,z_)
    
      x := x_
      y := y_
      z := z_
    
    PUB sumxyz(i,j,k) : sum
    
      sum := x[[i]] + y[[j]] + z[[k]]    'NEW [[index]] SYNTAX - accesses VARs by relative instance!
    
  • jmg wrote: »
    Heater. wrote: »
    But there is no way Spin code will compiler under Spin 2 anyway.
    Perhaps, but you can expect a lot of 'cut and paste' harvesting, and that can have TABs.

    Yup. My thought too.

  • I don't see anywhere you're specifying the type of the parameters being passed in to the SumXYZ function, so the Vals code is making the assumption that the caller is passing something of an expected type.

    If I'm reading this right, it looks like you're inferring the [[]] syntax to imply that the current object is part of an array, and using that as a way of self-referencing objects in an array by index. My gut feel is that's much more awkward than just passing a typed object handle around and referencing off of that, like this:
      typedef : "vals"
    
      Vals : v1, v2, vResult
    
    
    PUB DoIt : sum
      v1.setxyz( 1, 2, 3 )
      v2.setxyz( 4, 5, 6 )
    
      vResult.add( v1, v2 )
    
    
    // Vals object:
    OBJ
      typedef : "vals"
    
    VAR
      long x, y, z
    
    PUB setxyz(x_,y_,z_)
      x := x_
      y := y_
      z := z_
    
    'Note that the <name :> prefix here is denoting the type of the argument as another Vals object
    
    PUB add( Vals : arg1, Vals : arg2 )
      x := arg1.x + arg2.x
      y := arg1.y + arg2.y
      z := arg1.z + arg2.z
    
  • cgraceycgracey Posts: 13,631
    JasonDorie wrote: »
    I don't see anywhere you're specifying the type of the parameters being passed in to the SumXYZ function, so the Vals code is making the assumption that the caller is passing something of an expected type.

    If I'm reading this right, it looks like you're inferring the [[]] syntax to imply that the current object is part of an array, and using that as a way of self-referencing objects in an array by index. My gut feel is that's much more awkward than just passing a typed object handle around and referencing off of that, like this:
      typedef : "vals"
    
      Vals : v1, v2, vResult
    
    
    PUB DoIt : sum
      v1.setxyz( 1, 2, 3 )
      v2.setxyz( 4, 5, 6 )
    
      vResult.add( v1, v2 )
    
    
    // Vals object:
    OBJ
      typedef : "vals"
    
    VAR
      long x, y, z
    
    PUB setxyz(x_,y_,z_)
      x := x_
      y := y_
      z := z_
    
    'Note that the <name :> prefix here is denoting the type of the argument as another Vals object
    
    PUB add( Vals : arg1, Vals : arg2 )
      x := arg1.x + arg2.x
      y := arg1.y + arg2.y
      z := arg1.z + arg2.z
    

    This C-style syntax looks backwards and inside-out to me. I just don't get it. I'm going to have to stare at this for a little bit...

    Okay. I understand what it's doing, but concepts are scattered all over the place. I'm not used to thinking like that. I don't even want to think like that. Just shoot me.
  • cgraceycgracey Posts: 13,631
    Maybe I could learn to see the value in thinking like that. I see that "vals" were handily 'object'ified and made to do tricks on their own. Kind of cool. Syntax aspects of it are ugly, though.
  • Under the hood, the compiler knows that the thing being passed is an object handle (just an address). The Add() function is just accessing members of the object by name, which just turns into an indirection (address + member offset). The generated code isn't complicated - the compiler handles the bulk of the complexity.

    The compiler can also check to make sure that in the places you call this function, the type of thing being passed to the function matches the type that the function expects.

    The syntax here isn't perfect, I agree, but I was trying to convey the ideas quickly. In C, C++, Java, C#, and Pascal, you define types independent of objects, then create instances of those types. In Spin, you define the type AND create the instance at the same time, which limits how you can use them. The specifics of the syntax are less important than the decoupling of those two things.
  • You already support most of this in Spin - If I create an I2C object, I can call methods or accessors on it all over my code, but ONLY in the Spin object that creates it. Decoupling the definition and the creation allows you to pass that object around and use the same instance of the thing in many different places in the code, and it has the potential to make objects much more re-usable. There are a lot of objects that use DAT data to get around this limitation, but then it means you can only ever use one of them, so you have to duplicate the code two make two of something with different settings. This would eliminate that.
  • cgraceycgracey Posts: 13,631
    JasonDorie wrote: »
    ...The syntax here isn't perfect, I agree, but I was trying to convey the ideas quickly. In C, C++, Java, C#, and Pascal, you define types independent of objects, then create instances of those types. In Spin, you define the type AND create the instance at the same time, which limits how you can use them. The specifics of the syntax are less important than the decoupling of those two things.

    I see. I'll need to absorb this. This is heavy. I already quit reading the news because that was too stressful. Now this object stuff.
  • Chip,
    What about this variation of syntax? Does it make it simpler for you to grock?

    AnotherObject.spin:
    PUB Start()
      ' do some start stuff here
    
    PUB DoSomethingElse()
      ' do something here
    

    AnObject.spin:
    OBJ
      TYPE r_type : "AnotherObject.spin"
    
    PUB DoSomething( OtherObject )
      r_type[OtherObject].DoSomethingElse()
    

    main.spin:
    OBJ
      v : "AnObject.spin"
      r : "AnotherObject.spin"
    
    PUB main
      r.Start()
      v.DoSomething(@r)
    

    In the compiled version "AnotherObject.spin" only exists once and is associated with "main.spin". "AnObject.spin" has the OBJ TYPE declaration in order to be able to know the type for use in the DoSomething() method. OtherObject is a pointer and the r_type[] syntax is used to treat the pointer like an object. This matches the existing pointer typing mechanism for LONG/WORD/BYTE.
  • cgraceycgracey Posts: 13,631
    edited 2017-02-28 03:26
    Roy Eltham wrote: »
    Chip,
    What about this variation of syntax? Does it make it simpler for you to grock?

    AnotherObject.spin:
    PUB Start()
      ' do some start stuff here
    
    PUB DoSomethingElse()
      ' do something here
    

    AnObject.spin:
    OBJ
      TYPE r_type : "AnotherObject.spin"
    
    PUB DoSomething( OtherObject )
      r_type[OtherObject].DoSomethingElse()
    

    main.spin:
    OBJ
      v : "AnObject.spin"
      r : "AnotherObject.spin"
    
    PUB main
      r.Start()
      v.DoSomething(@r)
    

    In the compiled version "AnotherObject.spin" only exists once and is associated with "main.spin". "AnObject.spin" has the OBJ TYPE declaration in order to be able to know the type for use in the DoSomething() method. OtherObject is a pointer and the r_type[] syntax is used to treat the pointer like an object. This matches the existing pointer typing mechanism for LONG/WORD/BYTE.

    That's better. Maybe the TYPE keyword could go at the end of the line, though. Or, '=' could replace ':' in the OBJ declarations.
  • I'm liking this Roy.

    Hey Chip, can you explain your thinking about type being at the end of the line? It's fine. My question isn't about that, it's what contributes to that. Might help in these discussions.

  • cgraceycgracey Posts: 13,631
    edited 2017-02-28 03:29
    The lingering issue is that we need a double-long variable to hold a full object pointer. An object pointer could just be declared via 'VAR long Ptr[2]' and passed around as '@Ptr'.
  • potatoheadpotatohead Posts: 10,222
    edited 2017-02-28 03:33
    Would it make sense to add just one more type: address = long[2]?

    Seems to me, if we are gonna use pointers, those will be for objects and data. Why not package that up, so we don't have a bunch of typo and off by one errors out there? Edit: just objects, duh! Data is fine with a LONG. Got confused with WORD for a moment.

    DLONG Suitable for pointer, maybe some math results?
    LONG
    WORD
    BYTE

    Yes, I was gonna type PLONG above! Thought better of it, then thought it was funny, so here it is. :D The upside on that is humor probably will lock in what it means quick. There is that...




  • potatohead wrote: »
    Re: Tabs

    Let's allow them.

    This has long been a thorne in ye side of many a Python programmer, let us just solve it thee way they dust...

    Oh, never mind then.


    Seriously, this is just such a low level issue thats seemed to bedeviled the programming class since the early days.
    OK, tabs within "" or text should work as now.
    Tabs outside "", why not just show a character map [t] as a character map?

    Or, how about having a TAB field in every doc that simply denotes TAB=spaces(n).

    Or, simply an all black char map?

    Or, simply nuke Tabs all together?

    Or, is it really that big an issue ?
  • And eventually people understand tabs. Good as it gets IMHO, unless we take the key off the keyboard.
  • cgraceycgracey Posts: 13,631
    potatohead wrote: »
    Would it make sense to add just one more type: address = long[2]?

    Seems to me, if we are gonna use pointers, those will be for objects and data. Why not package that up, so we don't have a bunch of typo and off by one errors out there? Edit: just objects, duh! Data is fine with a LONG. Got confused with WORD for a moment.

    DLONG Suitable for pointer, maybe some math results?
    LONG
    WORD
    BYTE

    Yes, I was gonna type PLONG above! Thought better of it, then thought it was funny, so here it is. :D The upside on that is humor probably will lock in what it means quick. There is that...




    Good idea.
  • jmgjmg Posts: 14,847
    potatohead wrote: »

    DLONG Suitable for pointer, maybe some math results?
    LONG
    WORD
    BYTE

    With an ever expanding choice of platform-bits, (64b is now common) I've noticed a general move away from using LONG and WORD type naming, as that gets terribly confusing very quickly, as well as being non-portable/risky.
    Instead, types these days tend to include an explicit bit-count, making it very clear if you point to 8 or 16 or 32 or 64b data.


  • potatoheadpotatohead Posts: 10,222
    edited 2017-02-28 04:04
    I've noticed a general move away from using LONG and WORD type naming

    Maybe so, but those names are clear, simple, easily understood in terms of the BYTE. Would hate to see them go.



  • Regarding tabs - if you use them then you have to be willing to accept that eventually someone else will edit your file using different tab stops, convert the tabs into spaces, and save the file. If you use spaces, then someone might start using different indents, but at least the entire file won't change. At least the diff tools let one ignore whitespace. It's all somewhat maddening.
  • Total mess! Agreed.
  • The problem with allowing tabs is that they can get mixed with spaces. In the worst case, depending on a user's tab stops, the indentation could completely change, screwing up the program's semantics. Here's an example:

    Alice has her tab stops set every four spaces. But she inadvertently gets spaces mixed into her program:

    repeat 5
    <tab><sp><sp>if x
    <tab><tab>y := x

    which looks like this in her IDE:
    repeat 5
          if x
            y := x
    
    Brad loads Alice's program from the OBEX. But Brad has tabs set every two spaces. Here's what he sees:
    repeat 5
        if x
        y := x
    
    From Brad's standpoint, the entire meaning of the program has changed. Conclusion? DO NOT ALLOW TABS, PERIOD!

    -Phil


  • kwinnkwinn Posts: 8,695
    So don't get rid of the tab key. Use the ide/editor tab settings to determine how many spaces to insert for each tab key press. Convert incoming files with tab characters the same way.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,086
    edited 2017-02-28 04:58
    kwinn wrote:
    So don't get rid of the tab key. Use the ide/editor tab settings to determine how many spaces to insert for each tab key press.
    Yes.
    Convert incoming files with tab characters the same way.
    No. See above. Tab characters in a file should not count nor be displayed as whitespace -- ever. They should display as weird characters (as they do now) and cause a compile error, as appropriate.

    -Phil
  • cgraceycgracey Posts: 13,631
    potatohead wrote: »
    I've noticed a general move away from using LONG and WORD type naming

    Maybe so, but those names are clear, simple, easily understood in terms of the BYTE. Would hate to see them go.



    VAR types: BYTE, WORD, LONG, OBJ

    OBJ is a double long variable type that is only useful as an object pointer. In Spin2 methods, there is always an implied @ in front of it, so no need to type the @.

    To assign it a value:

    LONG [ObjVar][0] := PgmBase
    LONG[ObjVar][1] := VarBase

    To call an object with it:

    ObjType [ObjVar].Method(Params)
  • ElectrodudeElectrodude Posts: 1,440
    edited 2017-02-28 05:15
    cgracey wrote: »
    The lingering issue is that we need a double-long variable to hold a full object pointer. An object pointer could just be declared via 'VAR long Ptr[2]' and passed around as '@Ptr'.

    If I understand correctly, the two pointers are vbase and pbase.

    It only makes sense to use an object instance's VAR block with that objects' own methods.

    Therefore, if you make the first long of the vbase of each object a pointer to that object's pbase, object pointers can be made to fit in a single long containing a pointer to the object's vbase.

    pbase := long[vbase][0]

    This is the solution that most object-oriented programming languages use to fit object references in a single pointer. It's generally called a vtable, since it's used to store virtual methods. In Spin, all methods are virtual, i.e. they get looked up in the method table on every invocation.
  • Roy ElthamRoy Eltham Posts: 2,995
    edited 2017-02-28 05:39
    Chip,
    Couldn't we just change things so that the first part of an object's VAR area contains the object index (to access the code part), then your pointer just points to the particular VAR memory for the given object instance, and the first long there is a pointer to the object's code. Then the syntax is cleaner for the user, and we just have to make the connections in the compiler/interpreter.

    In any case, we can track everything in the compile and runtime(interpreter) to make the actual syntax for users simpler.

    Edit: Electrodude posted while I was typing and said effectively the same thing. Another thing this could allow is inheritance. ;)
  • Phil,
    Tabs should be allow for inside of strings, because there is reason to want them in communications with external stuff or whatever serial protocols might be needed.

    They just shouldn't be allowed as whitespace in code.
  • cgraceycgracey Posts: 13,631
    edited 2017-02-28 06:11
    Roy Eltham wrote: »
    Chip,
    Couldn't we just change things so that the first part of an object's VAR area contains the object index (to access the code part), then your pointer just points to the particular VAR memory for the given object instance, and the first long there is a pointer to the object's code. Then the syntax is cleaner for the user, and we just have to make the connections in the compiler/interpreter.

    In any case, we can track everything in the compile and runtime(interpreter) to make the actual syntax for users simpler.

    Edit: Electrodude posted while I was typing and said effectively the same thing. Another thing this could allow is inheritance. ;)

    I remember that coming up a long time ago. I figured it might inflate VAR memory for objects which have only a few VARs, but are instantiated in big arrays. I guess it's not really a problem, though, as there should be at least several VARs in any object, making the single-long-pointer-to-pbase not that big of a big deal.

    Okay, that sounds like the way to do it. Thanks, Roy and Electrodude.
  • Nice, so no new pointer type then?


    .
Sign In or Register to comment.