Norns: scripting

Thank you, I’ll check them out!

i actually forgot where i put it and googles “norns passthru” :slight_smile:

but the real answer is - not really, and that’s definitely a hole with the documentation. (getting people started with the SC side of things.)

1 Like

Is it possible to take a screencapture on the norns?

and here’s my image magick trick:

magick convert $1.png -gamma 1.25 -filter point -resize 400% -gravity center -background black -extent 120% $1-m.png
4 Likes

Perfect thanks :slight_smile: I started a series of basic tutorials and I want to augment it with screenshots.

7 Likes

[migrated]

the colon is just syntactic sugar. in above case, calling midi_signal:note_on(60,127) is the same as midi_signal.note_on(midi_signal, 60,127).

[context: midi_signal = midi.connect()]

in the libs, we always “declare instance methods” with a colon, and for “class methods” that don’t require an instance argument, we don’t pass one and therefore don’t use the colon.

the alternative would be to never use the colon and always show the instance arg explicitly. this would be non-idiomatic.

in other words, these functions are equivalent:

f = { x = 10 }

function f.inc(o)
   o.x = o.x + 1
   print(o.x)
end

function f:inc2()
   self.x = self.x + 1
   print(self.x)
end

and either function can be called with either syntax, regardless of how they’re defined:

> f.inc(f)
11
> f:inc2()
12
> f.inc2(f)
13
> f:inc()
14

AFAIK we never use the first form in the norns libs.

(maybe one thing that is confusing here is that you also have midi_signal.event = blabla. but .event is a field in the “instance” table, that stores a reference to a function.)

@andrew actually now that i’m looking, maybe what you mean is that it’s not completely at all obvious from the API docs which functions are instance methods and which are static functions. i’ve tried to explicitly state when a function is static, but i don’t know offhand how to explicitly tell LDoc to treat them differently.


update, oh. we should be using @classmod instead of @module. then it looks good. woops. i opened an issue.

3 Likes

yea, for sure it’s something I’ve wrapped my head around after getting into it, but like @neauoire it’s definitely something that I slipped up on when I was getting started. they appear pretty similar from the outside, without understanding the structure of the API (esp. since many languages take care of the distinction for u).

but yea I realize now there isn’t really a clean way around it. (the __index property is pretty handy for redefining what a property points to, but I don’t think it could be used as a workaround here, since it’s more about passing the right args ?)

maybe just something to keep in mind for future tutorials & studies n such

hm, i still don’t understand what you want to work around. the only issue i see is that we borked up the doc syntax.

(do you mean you just don’t want to use the colon syntax? too bad, that’s how lua does the equivalent of the implicit this pointer in C++. if you really hate it, you can explicitly pass the instance, like i showed above - lua doesn’t actually care. but our standard is to use the colon, which is less verbose and far more idiomatic.)

the __index property… [is] more about passing the right args ?

hm, i don’t see how. (first, i should clarify that if you’re just writing scripts, you probably shouldn’t have to care about metatables. but they are not very complicated.) the __index metatable has nothing to do with function arguments; it has to do with missing table fields. if you ask for foo.bar and foo doesn’t have a field called bar, it looks for foo.__index.bar. so this is a useful place to put class functions (both “static methods” and “instance methods.”) and in foo we put data. that’s the standard OOP model in lua, as shown above.

you can also use it for fancier stuff like custom accessors, but that’s a less common application.

yea, this would be the confusion point, personally it was a transition from what I was used to.

I kinda like it now because it makes me more aware of lower-level computer-ing processes I previously hadn’t been thinking about, but has the flipside of being another thing that a new programmer would have to wrap their head around. oh well !!!

as probably the least informed person who reads all these posts (at least in the running), i feel qualified to say that the colons still make almost no sense to me. even after reading the whole github issue where they were changed in 2.0 for certain (objects? classes? methods? instances? i don’t even know the right words) things.

the example above with the f.inc(o) and f:inc2() is probably the closest i’ve got to understanding, so thanks! it hasn’t clicked for me yet but i won’t give up.

i don’t know enough to know how it could be made easier for folks like me. i tried going through a high level object oriented programming class on lynda or somewhere but i was unable to translate it to lua norns world in my head.

i guess the simple answer is as long as it’s easy to find which things take a colon, i don’t actually need to know why it works.

if you’ve seen c++, you might have something like this:

class Foo { 
// every object of class Foo has a data field called "x" 
 int x;   
 
// a "class method" - doesn't refer to any particular object / data
   static void classMethod()
   {
      printf("doing some general Foo-related stuff");
   }

// an "instance method" - refers to a particular object of class Foo
    void instanceMethod()
   {
/// 'this' points to the particular instance of Foo that is being acted on
      printf("doing something with a particular Foo, value is %d", this->x); 
   }
};

and then you would use them like this:

Foo f;
f.instanceMethod();
Foo::classMethod();

in lua, you have . for class methods and : for instance methods.

(see this post above for how we declare a class in lua)

the equivalent would be

function Foo.classMethod() 
   print("doing general Foo-related stuff")
end

function Foo:instanceMethod() 
   -- 'self' is the equivalent of 'this' in C++;
   -- an implicit reference to the object (table) being acted on.
   print ("doing something with a particular Foo, value is " .. self.x)
end

-- you could also do it this way. this is really a static method,
-- but you explicitly give it a table that is assumed to be of type Foo.
-- this is how you would have to implement it in C, but it's not needed in lua
-- it's also more brittle b/c the user could mess up and pass something that's not a Foo.
-- if that happens, f.x could be nil and this could throw an error.
function Foo.anotherWay(f)
   print ("doing something with a particular Foo (hopefully), value is " .. f.x)
end

hope that helps.

sure. well first we gotta fix the docs so that it is in fact easy to find.

but it’s also basically important to know that the colon is for when you’re acting on a particular collection of data. (like, an object represnting a particular controller device.)

and the dot is for functions that don’t act on a particular object. these include functions that build new objects.

2 Likes

That’s amazing! Should I make a PR for the docs?

Sure! But look at the issue - I actually can’t figure out how to make ldoc do what is needed, it wants every function to be a method or not :confused:

My bad, I hadn’t seen the issue. This is a bit more complex than I thought.

What is the best way to include a relative file in a project repo, when you don’t know the name of the folder in which you are?

the global include() function, checks in dust/code and in the directory of the last loaded script.

but this does work with a file like a lib relative nested directory?

(I can’t find references to the various require/include conversations from 2.0 beta)

no, you’re right, it’s limited.

i don’t think it’s really possible for a chunk of lua code to know what file it came from, even if we hack the lua interpreter. (matrron/src/lua_eval.c)

the chunk is just a string compiled to bytecode. the string could be from anywhere.

that’s why we have the dust/code convention (_path.code global, a constant) and the concept of “current working directory” (norns.state.path global - updated when script is loaded)

I think what we’re chasing here is a convention in some scripts to use require - which needs more of a path, vs include which can be relative (I think)

local MeadowPhysics = require "meadowphysics/lib/meadowphysics"
vs
local MeadowPhysics = include("lib/meadowphysics")

The top one MUST be in a directory named meadowphysics. The bottom one seems to work but does not have the directory name problem.

See this mention for background.

EDIT - also the Dust 2.0 wiki is somewhat confusing on this because it interchangeably uses require and include - which should be mentioned as different things.

Also also - is dofile preferred over loadfile?

agreed. i think i’ve said this, but we should really discourage require. we use it in core libs, but in scripts it is a pointless optimization and actually hinders development of user libraries. (b/c caching)

3 Likes