Strict vs Anal

BlitzMax Forums/BlitzMax Beginners Area/Strict vs Anal

Adam Novagen(Posted 2012) [#1]
Hey all,

This month, I finally got myself some BlitzMax, and I have to say that right now I'm positively loving this brave new world. I'm diving in with the classic Beginner's Guide to BlitzMax, and currently getting comfy with the new OO style.

Here's the thing, though. Because of that, I'm finding that many of the conventions I was taught for Java are bleeding in, and I'm now questioning whether it's really necessary. For instance, I'm doing things like this:

Type ball
    Field x:Float,y:Float
    Field speed:Float
    
    Method moveBall:Int()
        setX(getX() + getSpeed())
        setY(getY() + getSpeed())
        Return
    End Method
    
    Method getX:Float() Return x End Method
    Method setX:Int(newX:Float) x = newX Return; End Method
    
    Method getY:Float() Return y End Method
    Method setY:Int(newY:Float) y = newY Return; End Method
    
    Method getSpeed:Float() Return speed End Method
End Type

As you can see, this is a bit verbose, but explicit and the very strictest of strict. I'm declaring the type of every function and method, even those which don't actually have to return anything, and I'm also using Return in all functions and methods rather than allowing the code to "fall out." I'm also using the long, command-like type declarations of Float and Int and so on, rather than using their respective shortcuts. In addition to that, I'm keeping all the custom type fields "private" by using get() and set() methods to access and alter them.

Obviously, this isn't absolutely necessary in Max; the above code could quite easily be written as this:

Type ball
    Field x#,y#
    Field speed#
    
    Method moveBall()
        x = x + speed
        y = y + speed
    End Method
End Type

Bob's yer uncle, eight lines to do the work of fourteen, a lot less typing even on the lines that DO remain, and it's still 100% strict Max-wise. (I always code with SuperStrict anyway, best to learn where the innermost rules lie.) So my consideration is this: while the "Java-style" Max coding is definitely the most conventional according to "better" coding practices, much more explicit, and indeed more familiar to me nowadays... Is it really productive here? Or am I just putting myself through unnecessary hoops out of habit?


Yasha(Posted 2012) [#2]
I'm declaring the type of every function and method, even those which don't actually have to return anything


If you drop the return type declaration, SuperStrict should interpret that as a "void" return type: i.e. you don't have to do this part and, in fact, it's a (very minor) type error.


Is it really productive here? Or am I just putting myself through unnecessary hoops out of habit?


The short answer is that the state of the art in PL design has yet to answer this question, so all answers are subjective.

My own opinion?

1) If you're going to use pseudo-properties (get'n'set methods), I'm pretty sure you can use BLIde to automatically generate these for you. This will at least save you some keystrokes.

2) Decide how important it actually is for something to be public or private, and make it one or the other. Most classes can afford to be fully public: there's simply no practical point in locking down fields that are either safe to modify directly, or that only you are ever going to see. Those that are intended for export and may need some regulation, you should lock down "properly" by removing the fields and property implementations to a private implementation class that extends the public abstract interface. Simply because there's nothing more pointless than properties on a class that can be modified directly by any old idiot.

3) Remember that BlitzMax is not Java. This isn't a trivial point: Java is intended and designed for the enterprise environment, where teams of dozens or hundreds of people work on a "live" project, without necessarily communicating fully, swapping classes in and out and hoping nothing breaks, etc.

BlitzMax is intended and designed for the indie environment: it is assumed that one person has total control of a project, and can read and understand the whole of their project's code. So BlitzMax provides some safety measures, but on the whole assumes that if "stupid" things are happening, it's because you have enough of a complete understanding of the code that they're probably intentional (e.g. 100% of things involving Byte Ptr).

4) BlitzMax's compiler doesn't inline any functions, even trivially small ones. Accessor methods therefore have a small but measurable performance impact. You may want to avoid them in tight loops and other important places.

Last edited 2012


Adam Novagen(Posted 2012) [#3]
Duly noted; makes sense. I think I'll carry on using Int and the like instead of % and the other shortcuts though, simply because they make code a bit more readable and quite frankly I like the geeky look of them. :]

Also, thanks for the tip about inlines; while I'm probably not going to do anything that requires such tight performance concerns any time soon, I always prefer my code to be efficient to the maximum, so it's good to know.


matibee(Posted 2012) [#4]
I'm not a big fan of get'ers and set'ers. They take a lot of typing and maintenance for very little gain in BMax (ie, no one is forced to use them even if you code them due to the lack of private fields or functions.)

I absolutely do however, do what you did with the second moveBall function - accessing fields directly in the types own methods.

I generally try to avoid manipulating fields outside of the type, so I guess I kind of do use setters, except I find a lot of fields change during other method calls rather than using dumb setters all the time.

I generally have no issues with reading type fields from outside the type, but treating them as read only (avoid the getter.) If it's a complex call that may later require some degree of control over it, i'll provide a getter method but more often than not it's not a getter of a field name but more of a descriptive method.. ie I might have a field called "celcius:Double" and a method called "getCelciusTemperature".

In order to see a public/private interface of a type just be scanning the code I use an underscore prefix in any types that *shouldn't* be called by anything other than the type itself...

ie
type Thing
  field x:Int
  field y:Int

  Method MoveThing()  ' treat this as a public method
  
  Method _scan() ' private method / func only for internal use
end type


These aren't all my ideas, but it's a clean way of working.


Derron(Posted 2012) [#5]
Getters and Setters should be used if you cannot trust the part which wants to modify the data.
Same for Filters like in:
Method SetLife(val:int)
  self.life = Max(0,Min(100,val))
end Method


Same is for Getters (if you want to make "overwriteable" or "default" things work).


In our bigger project I have a LUA coder on board who is able to access certain objects straight from Blitzmax.
We define Metadata there to limit access to fields/methods.
By Default all data is invisible, I can "publish" objects completely or selectively. The objects data can then be also made "writeable" (default would be just readable).
As sometimes it is easier to access certain methods than a long "chain" we have also overwriteable parameters in Getters which fall back to default behaviour if not given.
This is only possible with Getters and Setters.


If you like the "Get(myvarname)"-approach you can use TMaps to store the variable (I mean an "StorageObject" knowing about the type and the value) in it - but that is not the niftiest approach to do so but mimics magic methods of PHP.


Although it may sound a bit "strange" but using Getters and Setters makes the code more manageable as the underlying "speed = speed - wind" can be done in "GetSpeed()" - and if no longer needed we just adjust the "GetSpeed()"-method instead of adjusting the calculation in different parts of the code.

Also getters/setters are able to restrict access by certain userobjects. Or do things if they find something not initialised.


bye
Ron


Midimaster(Posted 2012) [#6]
To really have "private" Fields you can use the command PRIVAT. Only this prevents Fields from beeing accessed from outside.

BlitzMax-Help:
Rem
Private makes a variable, function or method only accessible from within the 
current source file.
End Rem

Public

Global	Score,Lives,Health

Private

Global	posx,posy,posz



Yasha(Posted 2012) [#7]
To elaborate on my own suggestion from post 2, point 2:



...this pattern will create "genuinely" private fields and methods, as long as the code is Imported rather than Included (the SuperStrict at the top should force the use of Import since it has to appear at the top of a translation unit; of course if the end user is modifying the source then all bets are off anyway as far as privacy goes).

It's really just a friendlier extension of the opaque pointer idiom.

It's also definitely not worth doing in code not intended for other people, since it requires a lot of keystrokes to achieve.

Last edited 2012