Forums

various cobra & .net questions re: static and global vars

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

various cobra & .net questions re: static and global vars

Postby DelphiGuy » Sun May 06, 2012 1:19 pm

folks:

the "program" below is a do-nothing-useful accumulation of every cobra/.net concept that's been confusing me for the last week as i struggle to learn cobra and .net. please forgive me for throwing so many issues at this forum in one posting, but i figure better to get it all over with at once.

please feel free to tell me to look up a certain .net concept, but if so please name it precisely. i have not been shy over the last week about looking up .net concepts as best as i can identify their relevance.

1) how am i to provide global vars, consts, types to every or any class in the entire program? let's assume a large program with multiple namespaces. yes, i realize that global vars are somewhat frowned upon, but even if i were to use some other approach, my underlying question is of scope/visibility: how do i get other classes to see, e.g., the Coin type, the someConstant constant, the ticksPerSecond property?

2) how do i access the MyRandom class from elsewhere in the program? sure, rather than learn how to deal with "shared" i can simply instantiate everywhere i need MyRandom, but i'm not wanting to repeatedly instantiate because i need base.init to do its job of providing a seed number to the .netRandom class -- such that any further calls to base.next (directly or indirectly) return the next number in the endless series of random numbers based on that one seed. i.e., i don't want to have more than one seed (i'm afraid that a fast program will result in identical seeds across multiple instances), and want any entity in the program who ever needs a random number to go looking for it from the same source, with the same one original seed.

thanks for any help, and sorry if the way i'm asking reflects fuzzy thinking.

-paul

namespace ESutils

class Globalz
enum Coin
heads, tails

const someConstant as uint32 = 37

var _ticksPerSecond as decimal

pro ticksPerSecond as decimal
get
return _ticksPerSecond
set
_ticksPerSecond = value

class SomeMain
def main
a = 3 + Globalz.someConstant
a = a + MyRandom.next(7, 8)
print [a]

class MyRandom inherits Random
cue init is shared
base.init

def next(lowValue as int, highValue as int) as int #fix next from random class,
assert lowValue <= highValue
assert highValue + 1 <= int.maxValue
return base.next(lowValue, highValue + 1)

def coinToss as Globalz.Coin
if .next(0, 1) == 0
return Coin.heads
else
return Coin.tails
DelphiGuy
 
Posts: 116

Re: various cobra & .net questions re: static and global var

Postby Charles » Sun May 06, 2012 6:26 pm

Technically, the word "global" in .NET is used for anything that isn't contained in a namespace. All of these are global:
class X

const answerToLife = 42

enum Coin
heads, tails

interface IDoSomething
def doSomething

sig MathMethod(a as int, b as int) as int

class Prog

def main
print X.answerToLife

As you can see above, constants are contained in classes (or structs) and accessed through them.

In the following code, none of the declarations are global because they are in the namespace NS. They are visible within NS and from any file that says "use NS":
namespace NS

class X

const answerToLife = 42

enum Coin
heads, tails

interface IDoSomething
def doSomething

sig MathMethod(a as int, b as int) as int


Let's say you wanted to have a global list of windows currently open in your GUI application. That would be something like:
class Application

var windows as List<of Window> is shared

cue init is shared
.windows = List<of Window>()

def main
trace .windows

class Window
pass

Syntactically, you can write the above as:
class Window
pass

class Application

shared

var windows as List<of Window>

cue init
.windows = List<of Window>()

def main
trace .windows

The above code uses "var" which is bad news. Anyone can modify the list at any time or even reassign it. In a threaded app, it could get even uglier. And as the app grows in size or number of people working on it, global vars become even less manageable. Basically all the classic problems with global vars. Here is a safer version:
class Window
pass

class Application

shared

var _windows = List<of Window>()

def addWindow(w as Window)
lock _windows, _windows.add(w)

def hasWindow(w as Window) as bool
lock _windows, return _windows.contains(w)

def removeWindow(w as Window)
lock _windows, _windows.remove(w)

get windows as IList<of Window>
""" Returns a new list of windows at the time of invocation. """
lock _windows, return _windows.clone

def main
trace .windows

Note that in Cobra, "_foo" is protected while "foo" is public (and "__foo" is private).

The general rule is to keep the variables protected or private and then control access to them through methods and/or properties.

And you can drop all the "locks" if you know your code isn't dealing with threads.

Re: random numbers, I usually just do this:
class AppWide

shared

var _random as Random?

get random as Random
if _random is nil, _random = Random()
return _random to !

class Prog

def main
trace AppWide.random.next

Here is a trick showing how you can access something as if it belongs to any object:
class AppWide

shared

var _random as Random?

get random as Random
if _random is nil, _random = Random()
return _random to !

extend Object

def random as Random
return AppWide.random

class Prog

def main
trace .random.next


class Foo

def bar
trace .random.next

I haven't needed to provide a seed to Random() as it seems pretty good at doing something different on each run. If, however, you needed consistency between runs, such as when doing simulations or testing game code, you could obviously have AppWide read the seed from the command line, an environ var, a config file, etc.

Finally, I keep my use of "shared" to an absolute minimum. Non-shared/global data and objects are generally easier to manage and get you into less trouble for all the classic reasons that have already been written about. I often consolidate "shared" items in a class called "AppWide" which is generally small. But sometimes the "shared" item in question belongs in the class it relates to.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: various cobra & .net questions re: static and global var

Postby DelphiGuy » Sun May 06, 2012 9:36 pm

charles:

thanks for the lengthy reply, which i have printed out and will study over the next couple of days.

a quick comment in the interim: the random init, if left without an explicit seed chosen by you and passed in as an argument, creates one on its own using a time-dependent algorithm. that's why it seems different every time: because the default seed is chosen according to the current time.

-paul
DelphiGuy
 
Posts: 116

Re: various cobra & .net questions re: static and global var

Postby Charles » Sun May 06, 2012 10:46 pm

You're welcome. I knew about the time-based seed which seems common across most (and maybe, all) standard libraries.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: various cobra & .net questions re: static and global var

Postby hopscc » Mon May 07, 2012 1:48 am

You're probably aware of this but given that the ticksPerSecond property is merely a wrapper for the _ticksPerSecond backing variable
you can just collapse the code to
var _ticksPerSecond as decimal
pro ticksPerSecond from var

# or implicitly get the backing variable generated
pro ticksPerSecond as decimal from var

( the above gets expanded to the equivalent of your code anyway)
Generally you only need the full property form if the code blocks for getter and settor need to do a little more than just reflect
in or out a variables contents ( synthesize or calculate values, more validation code, trigger effects elsewhere (GUI update, Observers,...)

1) How to provide vars constants types to widespread program parts?
- import the namespace containing the values and access directly - namespace contents here are usually just Types, and enums and shared/static providers ( the enum could be pushed up to the Namespace level)
import ESUtils
...
a = Globalz.someConstant + 3
a = a + Globalz.MyRandom.next(7, 8)

#probably clearer in client code to give the full namespace and Object spec ( no import)
a = ESUtils.Globalz.someConstant + 3
a = a + ESUtils.Globalz.MyRandom.next(7, 8)


b) Make the container object a Singleton - an Object that has only one instance no matter how many times it is 'instantiated'
Basically you hide the constructor so it cant be created outside the objects class, create another shared method that does the instantiation
storing the single instance local to the class - initial instantiation creates the object and stores it and returns the stored instance, subsequent ones just return the singleton instance.

For simple providers its common to mix the instantiation and use of singleton value in a single (shared) method
..
for your example (roughly)

class MyRandom inherits Random
shared
var _instance as Random # = Random()

cue init
base.init

def next(lowValue as int, highValue as int) as int #fix next from random class,
assert lowValue <= highValue
assert highValue + 1 <= int.maxValue
if not _instance # first call
_instance = Random() #, instantiate the singleton instance with whatever ctor values desired
#every other operation use singleton instance to provide the operation
return _instance.next(lowValue, highValue + 1)


# elsewhere use as

a = a + MyRandom.next(7,8) #if imported
...
b = 3 + Utils.MyRandom.next(10,20) # MyRandom in Utils namespace


Singletons are these days generally seen as a bad thing as they are still just basically dressing up a global variable/instance behind an OO facade. They're somewhat fragile in the face of changes since you often need change the caller code. ( google for 'Singletons considered harmful')


c) Make a Factory class that is responsible for providing the Object to be worked on via a factory method.
This is usually just another static class with a single static object returning method ( the factory).

A Factory is currently seen as superior because it is all code and amenable to extension/augmentation (inside the method ) without affecting the API call and or can be reconfigured to do something different if its execution environment (env variables, Property lookups, DB lookups ) is tweaked to generate different results. Also the strategy for the factory behaviour can often be provided in the factory rather than the returned class itself...

again very roughly
Namespace Factory
class Random
'''Factory class for production of Random generators of various capabilities all inheriting from System.Random()'''
var _single as Globalz.MyRandom

'''Singleton-ness functionality provided by factory'''
def single as System.Random is shared
if not _single
_single = Globalz.MyRandom() # passing any specific args
#_single = NonProduction.MyOtherRandom() #alternative randomGenerator
return _single

'''Alternative triggered by a string selector'''
def generator(pars as List of String) as System.Random is shared
if pars.contains('myrandom')
return Globalz.MyRandom()
if pars.contains('std')
return System.Random()
if pars.contains('test')
return TestRandom()
if pars.contains('bad')
return RandomNextAlwaysReturnsOne()

throw FallThroughException('Parameter not in "myrandom", "std", "test" "bad"')

'''Alternative triggered by a an underlying selector not part of caller API'''
def generator() as System.Random is shared
par = System.Environment('WhatRandom')
# could also read this from a properties file or other configurable lookup
if par == 'myrandom'
return Globalz.MyRandom()
if par == 'std'
return System.Random()
if par == 'test'
return TestRandom()
if par == 'bad'
return RandomNextAlwaysReturnsOne()

throw FallThroughException('Config error: getRandomGenerator parameter not in "myrandom", "std", "test" "bad"')


# Use like

a = a + Factory.Random.single.next(7, 8)
...
# or more likely
randm = Factory.Random.single
...
a = a+ randm.next(7,8)
...
b = 3 + randm.next(8, 16)




d) Charles solution - attach a handle/accessor/method/property for the useful thing to a common item (Object in this instance) using 'extends'.
This could be itself a factory generating an instance of the Object desired.




2) how do i access the MyRandom class from elsewhere in the program?
- pass the instance to the class to use
- instantiate and use the instance in the class - either as a normal instance or a Singleton
- access as a static method on a static class
- call a Factory for the instance (of whatever stripe is required) and use whatever is returned


Theres an old CompSci rule of thumb that
'Every computing problem can be solved by adding another layer of indirection'
apply that - creatively :D
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: various cobra & .net questions re: static and global var

Postby DelphiGuy » Wed May 09, 2012 7:14 am

hopscc + charles:

you guys have just provided me with a 13-page booklet for me to print out and study. which is exactly what i'm doing. thanks for such patient and customized support. i'll probably have some questions as i work through all the provided info. first thing that stands out: my problems are apparently mostly .net and general OOP issues, rather than specifically cobra issues.

some preliminary questions: what are "sig", "trace" and "pass", in the context of charle's provided code? if they're the cobra version of standard .net concepts, what please are the precise names of the .net concepts (so i can look them up without dragging you guys through explaining them)? "trace" and "pass" appear in so many google results as to make them effectively unsearchable in any meaningful way, so i'll need a little guidance as to context. thanks.

-paul
DelphiGuy
 
Posts: 116

Re: various cobra & .net questions re: static and global var

Postby Charles » Wed May 09, 2012 8:29 am

"sig" stands for "signature" and is for defining method signatures which you can then use as a type for arguments. You can then pass references to methods. It is covered in Delegates and briefly mentioned in MethodInvocation. Also, I have now linked the word "sig" at Keywords.

In .NET, these are called "delegates". Why they didn't call them "method signatures" and "method references" is beyond me. I first heard the term "delegate" used in the Objective-C community in the 90's for something different (and more appropriately named).

"pass" is specific to any language that uses indentation for code structure and is not a .NET concept. It's purely a syntax issue. Whenever you have a "class" or "if" statement or such, but you have nothing to put inside it, you put a "pass". It's a no-op. The equivalent in C# and Java is an empty block: "{ }"

"trace" is specific to Cobra. In Python, I would end up writing:
Code: Select all
print 'customer.balanceDue =', customer.balanceDue
# or even:
print 'FILE:LINE customer.balanceDue =', customer.balanceDue


Which in Cobra, is simply:
trace customer.balanceDue
# you can have more that one item:
trace x, y
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: various cobra & .net questions re: static and global var

Postby DelphiGuy » Wed Aug 08, 2012 8:20 am

Code: Select all
class Global
   const A = 123
   const A_again = 123
   const B = 456


Is there a Cobra syntax that allows for either one "const" block so that I don't have to repeatedly type "const" in a long list of constants? or that allows A and A_again to be condensed into one expression? Or that allows multiple "const" declarations on one line? Thanks.

Looks like I'm going to have long lists of consts, many with identical values.
DelphiGuy
 
Posts: 116

Re: various cobra & .net questions re: static and global var

Postby Charles » Wed Aug 08, 2012 12:00 pm

Cobra declarations (not counting the innards of a method) always start with a keyword followed by an identifier. "class X", "const foo", "def main", etc.

However, this looks like a job for enums, which would make better organization and less typing:
enum Pitch
A = 123
A_again = 123
B = 456

When you start loading up a Global class with too many things, I fear that spaghetti code is not far behind...
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: various cobra & .net questions re: static and global var

Postby DelphiGuy » Wed Aug 08, 2012 2:05 pm

Done. Thanks.
DelphiGuy
 
Posts: 116


Return to Discussion

Who is online

Users browsing this forum: No registered users and 15 guests