Passing an array as a RETURNed parameter

I'm pretty sure this has been discussed before, somewhere, but I can't find it in a quick scan of the forum. According to my reading of the BBC BASIC specification this program ought to work - and it does in my BASICs (not through any specific coding to make it work, but simply because it arises from the normal operation of the interpreter):
   10 PROC1(a())
   20 PRINT a(5)
   30 END
   40 DEF PROC1(RETURN a())
   50 DIM a(10)
   60 a(5) = PI
   70 ENDPROC
However it doesn't work in ARM BASIC V or in Matrix Brandy, albeit that they fail in completely different ways:

ARM BASIC V: "Undimensioned array in line 20"
Matrix Brandy: "The dimensions of array a() have not been defined at line 10"

(ARM BASIC fails after the procedure has been called, Brandy before)

I don't see how this code breaks any 'rules' of BBC BASIC:
  1. Passing an entire array to a procedure is fine in BBC BASIC, with the syntax I have used.
  2. Passing an uninitialised variable to a procedure is fine, so long as it is declared as a RETURNed parameter (in that case it is created within the procedure).
  3. Dimensioning an array inside a procedure is fine, whether it is a LOCAL or global array.
It's because of these 'rules' that the code works in my BASICs with no extra effort, and by my reckoning should work in any compliant interpreter. That it doesn't work in either ARM BASIC or Brandy I find very puzzling, and annoying given how useful this technique has been to me (not least in creating library functions with opaque array parameters).

ARM BASIC's "Undimensioned array in line 20" doesn't make sense because it was DIMensioned inside the procedure and then RETURNed. Matrix Brandy's "The dimensions of array a() have not been defined at line 10" doesn't make sense because it's declared as a RETURNed parameter so there's no requirement that it should be defined.

Comments

  • I'm pretty sure this has been discussed before
    Does the lack of response mean that this has indeed been discussed before? Can somebody point me to that discussion so I can remind myself what the conclusion was?

    I can think of only two plausible reasons for the code I listed not working. Either (1) there is code in the interpreters specifically to stop it working, but why would a useful feature be blocked? or (2) arrays aren't passed to, and RETURNed from, procedures in the same way that scalar variables are. But why wouldn't they be, isn't that just extra work for no benefit?

    Perhaps somebody other than Michael could reply, to prove that this forum really does have more members!
  • Sorry, no, I've been rushed off my feet with work stuff and sorting things out for my kid.
  • Perhaps somebody other than Michael could reply, to prove that this forum really does have more members!

    I'm reading, and your reasoning looks correct to me, even though on first sight the program looked odd to me. But then you're an expert and I'm not. Whether the reasoning shows the way for Matrix Brandy to follow suit without too much difficulty is a question for others - probably for Michael!

    It might be good to have an illustration here in this thread of the kind of useful library function you mention.
  • [Richard Russell]
    edited February 26
    BigEd wrote: »
    It might be good to have an illustration here in this thread of the kind of useful library function you mention.
    The concept of 'opaque structure' or 'opaque array' (and admittedly it's more generally applicable to structures than to arrays) allows multiple related library functions to share context by virtue of the calling program receiving that context from one and passing it to another, without itself knowing anything about its internal details. Hence 'opaque': the calling program doesn't know, and doesn't need to know, what's inside; it's just the 'messenger'.

    For example suppose you have a set of library functions which are designed to work with a specific kind of file, let's assume an image file. You might have one function which opens the file, another which allows you read the colour palette from the file, another which allows you to read the image dimensions, another which reads a particular pixel etc.

    Each of the functions which operates on that image file is going to need to know things like the image format, the image size, the bit depth etc. This information will typically be contained in a file 'header' of some sort. It would be hopelessly inefficient for each function to re-read the header, so you want some way of passing all that data around so it's readily available to each function.

    Typically you might have a function which opens the image file and returns to the calling program an array containing all the relevant context information read from the header. Then each function operating on that image file gets passed that array, so it knows everything that it needs to know about the file. The calling program, however, knows nothing about the contents of that array.

    To implement this kind of scheme in BBC BASIC you need a way for the calling program to pass an un-dimensioned array to the function which opens the file, within that function the array is DIMensioned and populated with the necessary data, and then returned to the calling program. That 'just works' in my BASICs, it never occurred to me that it wouldn't (despite the interpreter not having been specifically coded to support it) but it doesn't work in ARM BASIC or Matrix Brandy.
  • [Richard Russell]
    edited February 27
    The concept of 'opaque structure' or 'opaque array' (and admittedly it's more generally applicable to structures than to arrays) allows multiple related library functions to share context
    Edit: Rephrased for clarity:

    Object Oriented languages use exactly this technique (albeit using an opaque structure rather than an array) to pass the object to the individual methods so they have access to the shared object properties. BBC BASIC isn't Object Oriented, but my modern BASICs do have support for that paradigm via the classlib library, which relies on this working.
  • [Richard Russell]
    edited February 28
    arrays ... passed to, and RETURNed from, procedures in the same way that scalar variables are.
    You might reasonably ask, how do we know that arrays are internally represented in a way that is compatible with this?

    Apart from common sense suggesting that the design of the interpreter should be made much easier if they are, in fact this information is exposed in the documentation of the CALL statement.

    CALL creates a 'parameter block' in memory in which each variable (or array) is represented by two values: a type and an address (pointer). These basically tell you everything you need to know about the object concerned.

    The type values aren't consistent between different implementations (for example in my BASICs an array is flagged by setting bit 6, in Acorn's by setting bit 8) but it demonstrates a compatible representation of scalar variables and arrays.

    Of course there is a 'slight' complication that Matrix Brandy doesn't implement CALL, but I assume it still uses the same type + pointer representation internally otherwise that would be very odd.