I’m not sure how many more song inspired Fire references I can keep coming up with, but here at least is one more.
In response to my previous post Wouter commented that he had a much simpler cross platform solution to the random number problem:
program random; begin Randomize; Writeln(random); end.
Which is very pithy but actually makes a very good point for me, one which I was coming to anyway.
Pascal is, as it were, one of the old guard in the programming language battalion. And in particular the earliest implementations were extremely simple, relative to modern languages, intended (famously) to teach programming. That is, programming in the state of the art as current at the time.
Modern Pascal dialects actually have very little in common with those early incarnations of Pascal whilst the platforms on which those dialects run have become significantly more complex (and capable).
Yet, as simple as it is, the simple code above does not compile in Oxygene.
Surely a major shortcoming ?
Well, only if you intend to neglect the capabilities of the modern platforms on which your code runs today.
A lot of things that a Pascal developer might take for granted in the Pascal RTL are simply not present in Oxygene. In much the same way that many parts of the ANSI C RTL are not present in C# (or, more accurately, .NET). Or rather, the equivalent, modern capabilities are present but not in a syntactically compatible form.
.NET Random vs Objective-C Randomness
I promised last time that the next thing we would do would be to implement a .NET version of the simple RandomNumber program, re-using the shared code we created to contain a cross platform RandomNumber generator class. However, there is a problem we need to address first.
Anyone familiar with random number generation on .NET or Java should have seen it coming.
I had said that the shared RandomNumber class only needed to provide class methods. And this was true whilst we were exposing (essentially) global functions for seeding a global PRNG and generating random numbers from it.
But in .NET, there are no such global functions. There is instead a Random class which must be instantiated. The same is also true in Java.
But this is not quite the inconvenience it might first seem. There are some benefits.
For example, it means you can have multiple independent random number sequences, even with their own independent seeds. Why would you want to do this ? Why would you even want multiple random number generators operating independently let alone with different predictable (or unpredictable) sequences ?
I honestly don’t know. But I can imagine a scenario in a game, for example, where you might want a predictable sequence of random numbers involved in generating level layouts, so that a player would always get the same layout for each level in turn. But at the same time you might then want a “more random” sequence to trigger events during the game so that the gameplay was not replicated each time those predictable level layouts were played.
But that’s just a plausible use case. Not one I’ve personally used or even come across.
In any event, my initial shared RandomNumber class is currently not fit for that potential purpose. So let’s fix that.
Since we are looking to achieve a uniform programming “surface” on the class, I shall model my revised implementation on (a sub-set of) the .NET class. Checking against the Java Random class (same name, different platform) I find that this has almost identical methods to support the interface I have in mind.
Specifically, I shall have a parameterless constructor which shall seed using the current time, another constructor which accepts an integer seed and finally a method to generate and return a random number limited to an integer range:
type RandomNumber = public class public constructor; constructor(seed: Integer); method Next(limit: Integer): Integer; end;
First, let’s get this working for NOUGAT. That is OS X.
It turns out that those global functions (random() and srandom()) can be used in conjunction with a state buffer to manage multiple, independent PRNG’s.
All we need is a buffer to maintain the state for each PRNG and ensure that we apply the applicable state prior to generating numbers (using setstate()).
The state buffer is simply a suitably dimensioned AnsiChar array. The state buffer is initialised before use (with initstate()) part of which involves providing the seed. Since each instance of our class shall represent a discrete PRNG, we just need a member variable to hold that state buffer and appropriate initialisation of the buffer in the constructors. We might as well go ahead and also implement the Next() method while we’re at it, applying the instance state before generating the random number:
type RandomNumber = public class private fState: array[256] of AnsiChar; public constructor; constructor(seed: Integer); method Next(limit: Integer): Integer; end; implementation constructor RandomNumber; begin constructor(Integer(time(NIL))); end; constructor RandomNumber(seed: Integer); begin initstate(seed, @fState, length(fState)); end; method RandomNumber.Next(limit: Integer): Integer; begin setstate(@fState); result := Integer(Random mod limit) + 1; end;
You may notice this time – for clarity – I have not mixed-in the conditional compilation for the other platforms (ECHOES or COOPER). Instead let’s look at the complete implementation for each of those separately.
To implement these in both cases I shall use a feature, unique to the Elements languages: Mapped Types.
Mapped Types
A Mapped Type does not create a new distinct type. Instead it creates a synonym or alias for some other type. This is not a ‘wrapper’. The compiler is simply directed that whenever it encounters a reference to a certain type it should instead treat it as a reference to the other, mapped type. This means that you can write your code using the types you have declared in platform independent code, but if you need to pass the objects to some platform specific API you can do so, directly. No need for conversions or wrappers.
Similarly, within the Mapped Type you can declare methods and map those onto methods in the type being mapped on to. Again, these are not wrapper methods. When the compiler encounters a call to the declared method in the type it instead generates a call as specified in the “map” to the mapped type.
A mapped type is similar to a helper or extension class in that it cannot introduce new member data, but it can introduce new methods.
So with all that in mind let’s look at the ECHOES and COOPER versions of RandomNumber.
ECHOES (.NET)
RandomNumber = public class mapped to Random constructor; mapped to constructor; constructor(seed: Integer); mapped to constructor(seed); method Next(limit: Integer): Integer; mapped to Next(limit); end;
This goes in the interface section. There is no implementation required in this case, at all. The class maps on to the System.Random class and since this is the model for my mapped type, all of the required methods already exist in the mapped type, but I still need to declare the mappings for those.
When I reference RandomNumber I am only able to call the methods declared in the mapped type declaration, otherwise I could inadvertently create platform specific code, defeating the purpose of using a mapped type in the first place.
The exception to that are any methods on the platform specific base object class which have to be available.
So, what about the COOPER (Java) version ?
COOPER (Java)
RandomNumber = public class mapped to Random constructor; mapped to constructor; constructor(seed: Integer); mapped to constructor(seed); method Next(limit: Integer): Integer; mapped to nextInt(limit); end;
This is almost identical to the .NET mapping. The constructors are identical and provide the same initialisation. The only difference is that the method for obtaining a new, limited range integer is called nextInt, rather than Next, so this is addressed in the mapping.
Bringing It All Together
{$if NOUGAT} RandomNumber = public class private fState: array[256] of AnsiChar; public constructor; constructor(seed: Integer); method Next(limit: Integer): Integer; end; {$else} RandomNumber = public class mapped to Random constructor; mapped to constructor; constructor(seed: Integer); mapped to constructor(seed); method Next(limit: Integer): Integer; mapped to {$if ECHOES} Next(limit); {$elseif COOPER} nextInt(limit); {$endif} end; {$endif}
On NOUGAT we declare a new class which provides us with separate PRNG implementations.
On ECHOES and COOPER we have a class with the same name but which in these cases is merely a synonym for the existing classes on those platforms that already provide the separate PRNGs. Notice that the class name being mapped to is Random in both cases, with the appropriate namespace identified in the uses clause.
On all platforms the programming surface on the class is identical, allowing us to use this class in cross-platform code but still generating direct, native code in each case without the need for wrappers.
But now, whatever platform I am working on, if ever I need a random number I have a completely consistent and uniform mechanism for doing so, supporting the capabilities of the most capable platform (discrete PRNGs), not reduced to the lowest common denominator (a single, global PRNG).
Sweetening the Pill
Isn’t this all rather a lot of work just for getting random numbers ?
Firstly – not really. I am labouring numerous points here for the purposes of illustration. As you can see, the actual work involved borders on the trivial.
Secondly – I’m making more work for myself that I need to, again for the purposes of illustration.
There is an open source library for Elements containing a large number of mapped types and other cross-platform helpers. It is called Sugar, it’s hosted on github and indeed it already contains a class for random numbers (though it actually takes a different approach to my example).
So, just one last thing to do before we can move on to creating the .NET and Android apps to use this facility.
Refactoring the NOUGAT UI
We need a reference to an instance of RandomNumber, so we’ll add that to our private members. We can only generate random numbers when this reference is assigned, so I will ensure that the Generate button is initially disabled and only enable it when the Seeded button is pressed (which will instantiate the RandomNumber).
Since I’ll need to change the state of the button at runtime, I now also need a reference to that button so I add an outlet for that as well, connected to the appropriate button in Xcode Interface Builder:
private fRandom: RandomNumber; [IBOutlet] lblNumber: NSTextField; [IBOutlet] btnGenerate: NSButton; [IBAction] method seed(Sender: id); [IBAction] method generate(Sender: id);
“Seed”ing now means instantiating a new RandomNumber using the parameterless constructor, so we modify the seed() action method accordingly and also enable the generate button:
fRandom := new RandomNumber; lblNumber.stringValue := 'Generator Seeded'; btnGenerate.enabled := true;
And finally of course, the generate() action needs to be modified to obtain a value from the current fRandom:
lblNumber.intValue := fRandom.Next(100);
Done.
So next time we’ll build the .NET and the Android app and see how we use Fire in those cases.
Footnote on Cryptographic RNG
None of the above deals with cryptographic RNG’s. But the same technique could easily be employed to create a CryptoRNG class to provide a cross-platform class for the cryptograhic RNG capabilities of each platform.