Page 1 of 1

Ruby syntax query

PostPosted: Thu Oct 08, 2020 10:46 am
by HughBanton
Ruby array loader.fsm
(567 Bytes) Downloaded 267 times
Some Ruby syntax I don't understand. (How unusual ;-) ) Apologies if this is a bit basic!

I want to be able to load an array with other floats & arrays. The single 'a[0] = @f0' is straight forward, that works in any language.

But in line 5, in 'a[5..7]', it doesn't seem to matter what the second number is, the array @f3 always loads from a[5]. That's OK, it's actually where I want it, but I can also write [5..99] or [5..0] and it still works - this is rather odd, no?

Re: Ruby syntax query

PostPosted: Thu Oct 08, 2020 5:44 pm
by trogluddite
I can also write [5..99] or [5..0] and it still works

Aah, but what do you mean by "still works"? If you watch carefully, the end result isn't quite the same (e.g. the resulting arrays aren't always the same length). Numbering the original array elements may help to clarify what's going on, as the identical zero element values help to conceal what's really happening...
Code: Select all
a ={|x| "original:#{x}"}

When using this kind of code in Ruby, I find that it helps to think of it as "bulk replacement" rather than "element assignment". The behaviour is always described accurately by...
1) Delete the indicated original element(s) from the Array, shuffling up all following elements into the empty space (non-existent or reversed indices are ignored).
2) Insert the new element(s) at the start position of the deletion, moving all following elements along to make room (or adding "nil" elements if the Array is too short).
(NB: In practice, parts of this may be elided for efficiency, of course - e.g. when only one element is involved, a simple assignment has exactly the same end result.)

The upshot is that if you want your destination Array to end up the same size that it started, and with "unnassigned" elements still in their original positions, the Range of elements and the Array of new values must be the same size (and since Ruby can't guarantee this, there isn't really any other implementation which would be consistent).

There's another common gotcha when using an Array as the source of new element values...
-- When assigning an Array to a single element ("a[5] = @f3"), the entire assigned Array becomes nested within the destination as a single element (this is how Ruby handles multi-dimensional Arrays - which "green" doesn't support at all, of course!)
-- When assigning to a Range of elements (even if it covers only one; "a[5..5] = @f3"), the elements of the source Array are assigned sequentially to elements of the destination (after room has been made for them). This is also done for Ruby's other way of addressing multiple elements; "array[start_index, count]".

All of this might seem odd to many people, but it makes more sense when you realise that Ruby can often treat Arrays as if they are lists of objects to assign sequentially. For example....
Code: Select all
x = [1, 2, 3]
a, b, c = x
# Now; a == 1, b == 2, and c == 3

To learn a bit more about that, a good start is to google "Ruby splatting" (seriously!)

Re: Ruby syntax query

PostPosted: Fri Oct 09, 2020 1:58 am
by tulamide
I'm having serious issues with my Windows 10, black screen only, had to go back to a system recovery point, but even then I had that black screen, and only after switching off 2 times, and hard reset 6 times eventually the desktop appeared. So, if I disappear from the internet for a while, you know why.

I addition to Trog, always keep in mind, that in Ruby everything is an object. In fact, Arrays never store values (no numbers or letters), they store objects (and not even that, they store references to those objects, but let's not get too deep)

But what is an array? Exactly, an object. So, if you point to an array, the object "array" will be stored, not the contents of that array.

The "splatting", Trog mentioned is caused by the use of a so called splat operator. In other functional languages this is known as "destructuring assignment".

Code: Select all
a =
a[0] = 1
a[1] = @f3[2, 3]

watch a

Replace your code with above one, then have a look at the info pane. Do you recognize it? First of all, there are array entries although we create an empty array. This is, because it will auto-generate the array structure. You don't need to define a number of entries when creating an array. If you do
Code: Select all
a =
a[99] = 1
it will generate 100 entries, with the last one being set the object 1. All others will be set to the object nil. That's ok though for your float array, as nil is converted to 0 for green floats/float arrays.
Secondly, you only have 2 entries. The numbers after 1 are enclosed in square brackets, which means they are an array. This array is stored at a[1]. Thirdly, it is not the full array of f3, but only 3 numbers.

The conclusion is, that Ruby looks at your code, recognizes a range for f3, and creates a new array that is filled with the objects at index 2, 3, and 4. This new array is then stored at index 1 in array a.

This is also true for a. Something like "watch a[2..4]" would show an array with the contents of a from index 2 to index 4. But then you are assigning objects to this range you created. At this point Ruby notices that you don't want an array returned, but fill a slice of the array. Now it goes into a structuring mode.
Code: Select all
a = [1, 2, 3, 4, 5]
a[2..4] = 6
Try this. You will notice that the array now reads [1, 2, 6] :o
Ruby noticed that you wanted to replace index 2, 3 and 4, but you only assigned one object. So it sets index 2, but since the other two are missing, they are deleted from the original array. Change the assignment line to
Code: Select all
a[2..4] = 6, 6, 6
Now all three entries from the original array are replaced.

But what if you assign more than you reserved entries for?
Code: Select all
a[2..4] = 6, 6, 6, 6, 6, 6
Well, as I said above, an array is auto-generating entries that are needed. Ruby fills the three entries you defined, but now there are still three objects left over. It generates entries and inserts them after the last defined one (here index 4). Index 5 gets the object 6, etc.

If you read closely, you noticed that I said "insert". To prove this, use this code
Code: Select all
a =
a = [1, 2, 3, 4, 5, nil]
a[2..4] = 6, 6, 6, 6, 6, 6

watch a
The last entry of the original array is "nil". And so it is after we made our assignments! All the sixes are inserted.

Other languages, like C, or Basic, etc., will throw an error instead of just generating new entries. I'm not sure what I prefer.

Re: Ruby syntax query

PostPosted: Fri Oct 09, 2020 4:48 pm
by HughBanton
Hey guys, fantastic tutorials! Thanks.

I played with it a bit more yesterday with an 'array size' monitor attached and I began to figure out what was really happening. Success now anyway, the particular array I was actually building consists of 26 floats and 3 arrays, total size 793. All good.

It replaces a spiders web arrangement I had using a string of green Append Array prims; I assume that the Ruby method is better all round? It certainly looks a whole lot neater and will be easier to modify in future. (I like modifying :lol: ) I have several others that need similar attention ...

Just one thing : I couldn't figure out what to do with Trog's a ={|x| "original:#{x}"} . Hmm. I'm usually good at cracking things like that but couldn't get this one - sorry!

Hope your PC crisis resolves soon tulamide, I've experienced enough of those myself to know how demoralising it can feel.


Re: Ruby syntax query

PostPosted: Fri Oct 09, 2020 7:00 pm
by trogluddite
HughBanton wrote:I couldn't figure out what to do with Trog's a ={|x| "original:#{x}"} . Hmm. I'm usually good at cracking things like that but couldn't get this one - sorry!

Sorry, my bad! :oops: That code will create an Array of Strings ["original:0", "original:1", "original:2"...], which is not very helpful when you're trying to output a Float Array. D'oh!

I blame habits picked up from using Ruby for too long. In a Ruby Array you can freely mix different kinds of content, and I usually use the "watch" method for debugging inside RubyEdits to avoid the need for debug output pins. In that case, the Strings would have made my intent clear - whereas a "green" output converts the Strings straight back to zeros, thus achieving precisely nothing at all (except for baffling everyone!!)

HughBanton wrote:I assume that the Ruby method is better all round? It certainly looks a whole lot neater and will be easier to modify in future. (I like modifying :lol: )

To quote one of the most revered coding gurus of all time...
Donald Knuth wrote:The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times; premature optimization is the root of all evil (or at least most of it) in programming.

If the Ruby is making your schematic easier to work with, I say stick with it for now. If there's a significant detriment to performance, you'll find out soon enough when you try to use your exports, and there's nothing to stop you from changing back to "green" for an optimised "public release" version later on if you feel the need. Or, to look at it another way - making it quicker and easier to tweak things without accidentally introducing bugs can also be a kind of optimisation (of your time and effort, if nothing else).

FWIW, Ruby is probably less efficient in this case, as Ruby "Float" objects are much more complex beasts than native 32-bit/64-bit float values, and Ruby arrays only contain indirect references to these objects. So, conversion between "green" and Ruby Arrays can involve a lot of costly memory transactions and copying. But ultimately, the proof of the eating is in the pudding, and if the impact isn't noticeable in practice, I really wouldn't lose any sleep over it.

tulamide wrote:I'm having serious issues with my Windows 10

Ouch! Hope you get back on your feet soon - we wouldn't want to lose you!

Re: Ruby syntax query

PostPosted: Sat Oct 10, 2020 10:31 am
by HughBanton
The problem I often have with Ruby is that it appears to manage the same thing through umpteen different methods. I invariably feel more comfortable and 'in charge' with dsp & assembler!

So, what happens with this one, concatenating strings? On the face of it the result is the same as before ...
Ruby array loader 2.fsm
(1.13 KiB) Downloaded 260 times

And here's something really weird, didn't know you could do this! (Takes a bit of forcing :!: )
Ruby array loader 3.fsm
(394 Bytes) Downloaded 269 times


Re: Ruby syntax query

PostPosted: Sat Oct 10, 2020 1:35 pm
by trogluddite
Yes, Ruby is a very different way of looking at things compared to DSP and ASM (or "Green" primitives)! While there are methods which have "aliases" (different names for the same thing), there are usually enough subtle differences to justify having several ways of doing the same thing.

However, I get the feeling from your examples that maybe what's baffling you most is the difference of "type systems". "Green" primitives, DSP, and assembly are all "strictly typed" - the "type" of data that a component can use is determined entirely by the component (or instruction) itself. An "add" primitive with "F" inputs (or assembly "addps") only ever adds values of the "floating-point number" type and returns a floating-point number, and an Array Builder primitive with "[F]" output only ever builds a "container of floating point numbers" type. If you feed one of these components with the wrong kind of data it either won't work or, in the case of most "green" primitives/connectors, FS will attempt to convert the data to the correct type before using it.

OTOH, Ruby code works the other way around - when you call a method, Ruby looks at what type of object you have, and does whatever best seems to fit that type of object. Each different type (or, in Ruby lingo "class") of object can have it's own version of a method (or none at all). If the method call doesn't make sense for the objects in hand, it will usually just fail (automatic conversions are rare, aside from between Float/Integer numbers). For example...
Code: Select all
a = 1
b = 2
c = a + b
# "c" now equals 3 - Integer summing.

a = "Hugh"
b = "Banton"
c = a + b
# "c" now equals "HughBanton" - String concatenation.

a = [1, 2]
b = [3, 4]
c = a + b
# "c" now equals [1, 2, 3, 4] - Array concatenation.

a = [1, 2]
b = "Banton"
c = a + b
# TypeError: can't convert String into Array.
# (An Array can only have another Array joined to it.)

a =
b = 2
c = a + b
# NoMethodError: No method "+" for Color.
# (There isn't any "+" behaviour defined for Colors!)

When you call a method using "dot notation" (@my_object.method_name()), the class of the object before the dot determines which behaviour you get, and for operators such as "+", it's the object on the left hand side (in fact, there's no difference - operators are just regular methods with "syntax sugar" to allow the familiar mathematical infix notation).

This type-system is known as "duck typing" (from the old saying; "if it waddles like a duck and quacks like a duck, then treat it as a duck"), and the methods/operators are said to be "polymorphic" ("many shaped"). As we've seen in the previous posts, Ruby Arrays are similarly versatile - there is only one, generic class of Array, but the elements can be of any type at all, and you can mix elements of different classes in the same Array. In contrast, "Green" has three "types" of Array; but they're "strict", only allowing homogenous lists of floats, integers, or strings respectively.

HughBanton wrote:So, what happens with this one, concatenating strings?

Whether for a RubyEdit or any other kind of primitive, if the type of "green" data does not match the type of an input connector, the data is converted to the connector type BEFORE anything else is done with it. So, in this case, conversion happens outside of the Ruby code, and the Ruby variables will contain Arrays of Floats. The Ruby code will therefore use the Array form of the "+" operator - so you'll get Array concatenation, not String concatenation.

Similarly, for RubyEdit outputs, FS will convert invalid Ruby data into the correct output connector type before it can be passed along to subsequent components. For example, a "green" float array output will convert non-Float Array elements to zero (or will output an empty float array if the Ruby code failed to produce an Array at all).

HughBanton wrote:...didn't know you could do this!

As with so much of it, the User Guide documentation of "green" type conversions (end of Chapter 5) is incomplete! For example, if you connect any kind of array link to an integer connector, you'll get the size of the Array. I don't know of any comprehensive list, so I can't even tell you for sure what's missing - even after all my time using FS/SM, I still get the odd surprise!

These different type-systems have another consequence: With "green" primitives, you pretty much can't connect things together in a way that will break anything - when a Float is expected, FS will make damned sure that it gets one! OTOH, Ruby doesn't hold our hands anything like as much - it's easy to get the dreaded "red boxes" and Ruby shut-downs if you lose track of what kind of objects you're passing around!

(PS: If mY Capitalisation seems a BiT strAnge: In Ruby, the names of classes of object are always in UpperCamelCase. I habitually do the same in prose when writing about code- but please don't rely on me being at all consistent about it when there's a class-name/generic-word ambiguity!!).

Re: Ruby syntax query

PostPosted: Sat Oct 10, 2020 6:56 pm
by HughBanton
Marvellous as ever Trog, many thanks. You should write a book!

Ruby looks at what type of object you have, and does whatever best seems to fit that type of object.

That Ruby classes contain multiple elements that automatically detect different data types is genius, I hadn't grasped that at all. Revelation indeed.

I'm at a 'tear it down, build it up again' point with my solitary organ project (it was about time ..) so I'm winding it all back and studying a whole bunch of things afresh. When I started in 2016/7 I knew next to nothing. My organ thingy has always been heavily 'Array Central' which is why I'm looking at all these other array methods right now.

As with so much of it, the User Guide documentation of "green" type conversions (end of Chapter 5) is incomplete!

We're all wondering when, but more particularly 'how', the whole FS4 manual is going to be re-written when Maik finally declares his epic work complete over on Slack. (?)

Now then, about this book you should write :lol: ......


Re: Ruby syntax query

PostPosted: Mon Oct 12, 2020 11:22 pm
by trogluddite
HughBanton wrote:Marvellous as ever Trog, many thanks. You should write a book!

Thanks; you're welcome. A book has been mooted a few times, and I'm always flattered that folks think I have it in me - but since one forum post can take me a few hours to write, I think we'd probably all be dead and buried before I finished chapter one! Without the focus of a definite question to answer, my olympic gold-medal standard procrastination would have a field day (to date: approx 13 years using SM/FS ==> 1 plugin ever, ahem, cough, cough, "finished"!).

HughBanton wrote:I'm looking at all these other array methods right now.

Well, there are certainly plenty to go at! The version of Ruby (1.8.6) inside FS 3.0.6 has 167 Array methods according to the complete list - paste this into an empty RubyEdit to see them all...
Code: Select all

Probably more useful is the official Ruby documentation (here's the page for Arrays). There will probably be a lot of jargon there that's new to you, but it should give you the jist of what kind of operations are possible - you can always come back here for clarification if any of the names or descriptions whet your appetite. Certainly, Ruby can do all sorts of amazing things with Arrays, often using only a single line of code where "green" would require dozens of primitives inside a loop structure (prone to generating excessive triggers unless you're careful!)

HughBanton wrote:We're all wondering when, but more particularly 'how', the whole FS4 manual is going to be re-written when Maik finally declares his epic work complete over on Slack.

I have to admit, this has me rather worried too. Having worked as part of a commercial team, I feel that waiting until release-ready before writing the docs is rather dangerous - if you don't write them as you go along, it's so easy to miss things out, and it becomes such an overwhelming task that it's very tempting to cut corners. I take it as a strong hint from fate that we've already had "false alarm" bug reports because folks have misunderstood how new features were intended to work.

At the same time, we have to realise that there can never be a definitive guide for any coding environment - so much depends upon what each user intends to create with it. Ruby and assembly are especially problematic, as they are entire coding languages in their own right - and the uniqueness of the FS environment means that "general purpose" learning resources can be tricky to adapt (a catch-22 even - comprehending the adaptions requires already knowing the basics of the language!) FWIW, I still think that folks intending to master Ruby would do well to do some learning the "traditional" way with stand-alone Ruby - it's likely easier in the long-run than trying to cobble together an ad-hoc "curriculum" in FlowStone (but another catch-22 - you need to know Ruby fairly well to be able to see whether this investment will pay off for your own flavour of projects!).

I also think that FS is crying out for a better way for us to share and archive user-created modules and tutorials. Slack just isn't good for that task, I don't think, and this place is getting very clunky for lack of support (I fear that it will just stop working one day when the spammers fill up the server with fake account details). Despite members' best efforts at salvage, we lost a huge amount of fantastic content, and the members who created it, when the SM site was trashed, and it would be a massive blow were that to happen again - all the more so after Maik has put in so much hard work to revive FS's fortunes.