I always begin by doing a
run_file in buffer #0 to load my functions and constants so that I can run those functions in my main working buffer #7. The main working buffer contains a lot of variables that I don’t alter, but mix and match them as parameters to my functions that were loaded in buffer #0. The main working buffer gets cluttered, but I do want all its materials available at all times, so I tried putting it all in a file, then doing an
eval_file on that file at the top of my working buffer. But then I get a runtime error when the first constant encountered in the
eval_file file is considered uninitialized. If the one-time
run_file file’s stuff is visible to everything in my working buffer, how can I make the
eval_file file aware of it? Inline, all the content of the file being eval’ed sees the loaded library, and I thought
eval_file just inlines its content, like an include statment. I don’t want to put the
run_file in the working buffer because it’s large, and I frequently re-load the working buffer when I change stuff in it as part of the performance.
I always begin by doing a
That is a big paragraph what would be super helpful, if you are happy to add it, is a minimal code example to demonstrate the issue, so that it is easily reproducable
I have a file named library-1.rb that contains the following:
CONSTANT = 1
In buffer #0, I execute:
I have a file named 1-1.rb that contains the following:
myArray =  myArray[CONSTANT] = 2
In buffer #7, I execute:
which results in the runtime error “uninitialized constant SonicPi::Lang::Core::CONSTANT”
But if I directly execute
myArray =  myArray[CONSTANT] = 2
in buffer #7, there’s no error.
I don’t understand why constants (and variables) in the library that’s been run_file’ed and that are known in buffer #7 aren’t known in the file that’s eval’ed in buffer #7. I guess I’m looking for an include-type statement that would just inline the text rather than eval it. Is there a workaroun?
Yeah I can confirm that I have reproduced that issue (on Windows).
One workaround is to use a
define for your constant in the run_file’d file - messy but seems to work for me.
define :constant33 do 33 end
Did you infact mean the second line to say:
In any case, @samaaron will be able to explain it better than I can, but I believe the issue here is that
eval_file does not namespace the constants like
run_file does. As far as I understand it, functions like
run_code (as well as code run directly in a Sonic Pi buffer) are placed in a special namespace behind the scenes, since this code is evaluated within a Sonic Pi ‘Run’.
Code called via
eval_file on the other hand, is evaluated directly, without being placed in the usual namespace - so there will be a scope mismatch for any constants that are defined in this manner.
Keep in mind of course that we never intended Sonic Pi to support constants in the ‘traditional’ sense - Sonic Pi is not Ruby
As for workarounds, @horza’s suggestion is one way. Another is to use the Time-State store to
get your ‘global’ values.
(P.S. - In case it’s useful, are you aware of the init.rb file? this would allow you to load arbitrary code on Sonic Pi start up, without having to manually Run buffer 0 )
run_file fixes the problem for SPI’s version of constants. I’d reduced my toy too much for my question. The problem remains with variables.
If my file 1-1.rb contains
myArray2 = [1, 2, 3, 4, 5]
then, in my buffer, I do:
run_file "<PathToFile>/1-1.rb" print myArray2
I get “undefined local variable” runtime error.I get the same error if I use
eval_file instead. Of course, if my buffer has:
myArray2 = [1, 2, 3, 4, 5] print myArray2
it works properly
By the way, init.rb isn’t practical for stuff I’m making incremental changes to for development purposes. In a previous thread, I think it was you who referred me to
run_file for this use case. For the current problem, I tried
eval_file as a guess, but the functionality I’m after is being able to put variables in an external file for convenience of having a lot of material on hand without cluttering up (or overruning) the buffer I’m working in.
Thinking this over, these materials I’m using as variables are in fact either 1) fixed data or 2) global data structures to be altered in a central location so that the change is effected simultaneously in all live loops using them. In the first case, I guess I can just rename them starting with upper case so that they function like constants Ruby’s differentiating constants from variables this way (and other naming conventions) has been a frequent source of grief due to trivial errors caused by force of habit from my using C back in the day.
For the second global data structures issue, I’ve just now glanced at the Time State docs, and that’s something I’ve overlooked completely up till now. I did read the documentation once through completely back when I started, but apparently I didn’t remember the Time State concept at all, plus the name “Time State” isn’t really suggestive of its function to my mind. I guess I’ll now study up on Time State and see how to re-work things to get to an efficient working methond in this different way.
Sure - as I understand it, in this case, the
myArray2 = [1, 2, 3, 4, 5] is a local variable definition, which means that it is out of scope beyond the file that it is defined in . It works when defined in line because the local variable is then in scope with respect to the following code.
Simultaneous posts. Check out what I just wrote.
Ok - look forward to hearing how you get on with that
Reading through the
get documentation, it seems that Time State doesn’t suit my situation, and that I should use constants. I’m interested in your comments in case I’m missing something.
I use several arrays with named indices like this:
myDataStructure =  myDataStructure[ArbitraryName1] = 1 myDataStructure[ArbitraryName2] = 2 ... myDataStructure[ArbitraryNameN] = N
myDataStructure don’t get all their slots initialized, just the slots that suit the particular moment during live coding, then my functions test for and operate on the non-nil slots. The named indices are crucial to keeping track of things, as is the option to use and omit any combo of the named slots. I make changes to myDataStructure via manual editing, then do a
cmd-r reload to effect the changes when I feel like it, musically speaking. For this kind of operation, there’s no use to putting a
set inside a live loop; I want to do a bunch of edits by hand, then reload.
It looks to me like Time State would be cumbersome to use—and perhaps not even possible due to the arbitrary omission of array slots—at live coding time because then a function call would look like this:
instead of the more natural, but not Time State-protected this:
Worse, typing colons and square brackets while live coding (regardless of how economically things are named) would be quite aggravating. To sum up, because these data structures are all treated like global constants rather than variables that I, not my functions, modify, I’m better off just capitalizing them and moving on. What do you think?
Fair enough, understandable. You can also use
get as a normal function (ie with round brackets,
get(...)) as opposed to using square brackets to index it. (Meaning that you could then rely on Ruby’s relaxed function calls to omit the round brackets:
get ... ). Also, you could have a workaround for the symbol keys by storing the keys in a variable So,
dataStructure = :myDataStructure # or a string works just as well 'myDataStructure' myFunction get dataStructure
Is that any more useful?
Yes, and keep the variable in a file so that it’s outside the buffer’s scope.
I’ll keep the relaxed syntax in mind in case I ever need to do something with Time State.