back to list

Making a javascript tuning theory library

🔗Mike Battaglia <battaglia01@...>

9/3/2011 7:42:11 PM

Preliminary consideration: Javascript allows you to create arrays
using [] literals. For example:

var syntonic = [-4, 4, -1];
var meantone = [[1, 1, 0], [0, 1, 4]];

A question: which of the following programming conventions would you
find to be most sensible?

Convention 1 (object-oriented, strongly typed):
var twelveequal = new Val([12, 19, 28]);
var nineteenequal = new Val([19, 30, 44]);
var meantone = new Temperament(twelveequal, nineteenequal);
var syntonic = new Monzo([-4, 4, -1]);
alert(meantone.map(syntonic)); //should alert "(0, 0)"

Convention 2 (procedural, loosely typed):
var twelveequal = [12, 19, 28];
var nineteenequal = [19, 30, 44];
var meantone = [twelveequal, nineteenequal];
var syntonic = [-4, 4, -1];
alert(map(meantone, syntonic));

I'm leaning towards convention #1. Any thoughts?

-Mike

🔗Wolf Peuker <wolfpeuker@...>

9/5/2011 3:04:47 AM

Hi Mike,
great idea, really!

Am 04.09.2011 04:42, schrieb Mike Battaglia:
>
> A question: which of the following programming conventions would you
> find to be most sensible?
>
> Convention 1 (object-oriented, strongly typed):
> var twelveequal = new Val([12, 19, 28]);
> var nineteenequal = new Val([19, 30, 44]);
> var meantone = new Temperament(twelveequal, nineteenequal);
> var syntonic = new Monzo([-4, 4, -1]);
> alert(meantone.map(syntonic)); //should alert "(0, 0)"
>
> Convention 2 (procedural, loosely typed):
> var twelveequal = [12, 19, 28];
> var nineteenequal = [19, 30, 44];
> var meantone = [twelveequal, nineteenequal];
> var syntonic = [-4, 4, -1];
> alert(map(meantone, syntonic));
>
> I'm leaning towards convention #1. Any thoughts?
>
> -Mike
>
I'd prefer #1, because its naming capabilities are richer.

Where do want to "host" it? Can I help you here anyway?
I hope to find all in the source that I don't get in the xenwiki. :)

--Wolf

🔗genewardsmith <genewardsmith@...>

9/5/2011 3:26:48 AM

--- In tuning@yahoogroups.com, Wolf Peuker <wolfpeuker@...> wrote:

> I hope to find all in the source that I don't get in the xenwiki. :)

All? There's a hell of a lot involved in programming "all". How ambitious is this project?

🔗Wolf Peuker <wolfpeuker@...>

9/5/2011 5:14:13 AM

Am 05.09.2011 12:26, schrieb genewardsmith:
>
>
> --- In tuning@yahoogroups.com, Wolf Peuker <wolfpeuker@...> wrote:
>
>> I hope to find all in the source that I don't get in the xenwiki. :)
>
> All? There's a hell of a lot involved in programming "all". How ambitious is this project?
>
Ok "some" I would have probably written better. Let's see - I believe,
it will grow over time (if it runs once).

Admittedly, I find it very difficult to understand the mathematical
notation, but using the source code I can go back to Adam and Eve if
necessary.

--Wolf

🔗genewardsmith <genewardsmith@...>

9/5/2011 8:32:03 AM

--- In tuning@yahoogroups.com, Wolf Peuker <wolfpeuker@...> wrote:
>
> Am 05.09.2011 12:26, schrieb genewardsmith:
> >
> >
> > --- In tuning@yahoogroups.com, Wolf Peuker <wolfpeuker@> wrote:
> >
> >> I hope to find all in the source that I don't get in the xenwiki. :)
> >
> > All? There's a hell of a lot involved in programming "all". How ambitious is this project?
> >
> Ok "some" I would have probably written better. Let's see - I believe,
> it will grow over time (if it runs once).
>
> Admittedly, I find it very difficult to understand the mathematical
> notation, but using the source code I can go back to Adam and Eve if
> necessary.

I've got a lot of Maple code, and Graham has Python script.

🔗Mike Battaglia <battaglia01@...>

9/5/2011 10:54:17 AM

On Mon, Sep 5, 2011 at 6:26 AM, genewardsmith
<genewardsmith@...> wrote:
>
> --- In tuning@yahoogroups.com, Wolf Peuker <wolfpeuker@...> wrote:
>
> > I hope to find all in the source that I don't get in the xenwiki. :)
>
> All? There's a hell of a lot involved in programming "all". How ambitious is this project?

It's not ambitious at all. Once I get vals and monzos coded up, which
is pretty easy, it's obviously not too complicated to afterwards code
up an "apply homomorphism" function, and a wedge product function, and
a complement function, and a normal val list function, and a TE norm
function, etc. It'll be pretty straightforward and I'll learn a lot by
having to recode this stuff.

-Mike

🔗Carl Lumma <carl@...>

9/5/2011 11:02:31 AM

In my opinion, Convention 1 is a classic case of too
much structure too soon and indeed, of OO (a good approach
for writing simulations, but not particularly for anything
else) gone wrong. -Carl

--- In tuning@...m, Mike Battaglia <battaglia01@...> wrote:
>
> Preliminary consideration: Javascript allows you to create arrays
> using [] literals. For example:
>
> var syntonic = [-4, 4, -1];
> var meantone = [[1, 1, 0], [0, 1, 4]];
>
> A question: which of the following programming conventions would you
> find to be most sensible?
>
> Convention 1 (object-oriented, strongly typed):
> var twelveequal = new Val([12, 19, 28]);
> var nineteenequal = new Val([19, 30, 44]);
> var meantone = new Temperament(twelveequal, nineteenequal);
> var syntonic = new Monzo([-4, 4, -1]);
> alert(meantone.map(syntonic)); //should alert "(0, 0)"
>
> Convention 2 (procedural, loosely typed):
> var twelveequal = [12, 19, 28];
> var nineteenequal = [19, 30, 44];
> var meantone = [twelveequal, nineteenequal];
> var syntonic = [-4, 4, -1];
> alert(map(meantone, syntonic));
>
> I'm leaning towards convention #1. Any thoughts?
>
> -Mike
>

🔗Graham Breed <gbreed@...>

9/5/2011 12:35:38 PM

Mike Battaglia <battaglia01@...> wrote:

> Convention 1 (object-oriented, strongly typed):
> var twelveequal = new Val([12, 19, 28]);
> var nineteenequal = new Val([19, 30, 44]);
> var meantone = new Temperament(twelveequal,
> nineteenequal); var syntonic = new Monzo([-4, 4, -1]);
> alert(meantone.map(syntonic)); //should alert "(0, 0)"

A Temperament object should involve a prime limit as well
as a mapping. You can then make error and complexity
methods of the Temperament class. Having a single
Temperament object, instead of a different one for each
rank, is also a good idea. You might still need
rank-specific subclasses for those measures that don't
generalize.

As Carl said, there isn't much point in defining Val and
Monzo classes that only take one object in their
constructors and don't have any new methods. It doesn't
have to be all object oriented or all procedural.

Note: it's theoretically easy to retrofit Val and Monzo
objects as subclasses of Array if you find you do need
them. Unfortunately there are problems in reality:

http://dean.edwards.name/weblog/2006/11/hooray/

Graham

🔗Mike Battaglia <battaglia01@...>

9/6/2011 2:52:43 PM

On Mon, Sep 5, 2011 at 2:02 PM, Carl Lumma <carl@...> wrote:
>
> In my opinion, Convention 1 is a classic case of too
> much structure too soon and indeed, of OO (a good approach
> for writing simulations, but not particularly for anything
> else) gone wrong. -Carl

Can you give an example of a syntax you'd like better? I really wish I
could define literals, e.g. something like

var tet12 = <12 19 28|
var syntonic = |-4 4 -1>
alert(<tet12|syntonic>) //should spit out 0
var tet19 = <19 30 44|
var meantone = tet12 /\ tet19 //creates, under the hood, a Temperament object

alert(meantone.wedgie()) //spits out "<<1 4 4||"
alert(meantone.normalValList()) //spits out "<1 1 0| <0 1 4|"
alert(meantone.normalCommaList()) //spits out "|-4 4 -1>"
alert(meantone.POTEGenerator()) //too lazy to look this up right now

That's what I really like, but since JS doesn't let you overload
operators and define new literals, not much of an option. Maybe a
suitable compromise would be something like

var tet12 = V([12, 19, 28])
var syntonic = M([-4, 4, -1])

That's at least a bit more syntactically palatable.

You know, there is this -

http://nixtu.blogspot.com/2011/05/using-jsshaper-to-provide-operator.html

If any of you real hip compsci people know of any sort of AST -> JS
and also AST -> Python type things, or some alternate but equally hip
solution, that would be a godsend in this case.

-Mike

🔗Mike Battaglia <battaglia01@...>

9/6/2011 4:06:04 PM

On Tue, Sep 6, 2011 at 5:52 PM, Mike Battaglia <battaglia01@...> wrote:
>
> http://nixtu.blogspot.com/2011/05/using-jsshaper-to-provide-operator.html
>
> If any of you real hip compsci people know of any sort of AST -> JS
> and also AST -> Python type things, or some alternate but equally hip
> solution, that would be a godsend in this case.

Actually, as much as I hate Microsoft, JScript-based ASP (not ASP.NET)
might be applicable in this case. The ultimate holy grail for all of
this would be to come up with a both client- and server-side library,
so I can have both crazy real time interactive Web 2.0 temperament
widgets, but also so we can make it so that the Wiki can support
something like this

[temperament name=meantone comma=81/80 limit=5]

or some more suitable variant, and it would then autogenerate a huge
template for meantone. Then, if we decide we want to start including
TOP generators as well, a simple library change changes the output for
every temperament on the site simultaneously. And if we want to have
an icon that, when you click on it, plays an autogenerated minimal
comma pump using Gene's algorithm and data: URIs, that's a day or two
to code up.

This would be rather easy to do in JavaScript after developing
something like the aforementioned library, but I'm worried that there
are enough people around here with browsers that don't support JS that
the Wiki would be rendered entirely incomprehensible for them.
Although one could always just put the manual printout in <noscript>
tags, as a first pass for all of this.

-Mike

🔗Carl Lumma <carl@...>

9/7/2011 12:59:54 AM

Mike Battaglia <battaglia01@...> wrote:

> > In my opinion, Convention 1 is a classic case of too
> > much structure too soon and indeed, of OO (a good approach
> > for writing simulations, but not particularly for anything
> > else) gone wrong. -Carl
>
> Can you give an example of a syntax you'd like better?

I liked Convention 2 better... :) -Carl

🔗Graham Breed <gbreed@...>

9/7/2011 4:28:25 AM

Mike Battaglia <battaglia01@...> wrote:

> That's what I really like, but since JS doesn't let you
> overload operators and define new literals, not much of
> an option. Maybe a suitable compromise would be something
> like
>
> var tet12 = V([12, 19, 28])
> var syntonic = M([-4, 4, -1])

How about

var tet12 = [12, 19, 28]
var syntonic = [-4, 4, -1]

?

Graham

🔗Mike Battaglia <battaglia01@...>

9/7/2011 5:00:09 AM

On Sep 7, 2011, at 7:28 AM, Graham Breed <gbreed@...> wrote:

Mike Battaglia <battaglia01@...> wrote:

> That's what I really like, but since JS doesn't let you
> overload operators and define new literals, not much of
> an option. Maybe a suitable compromise would be something
> like
>
> var tet12 = V([12, 19, 28])
> var syntonic = M([-4, 4, -1])

How about

var tet12 = [12, 19, 28]
var syntonic = [-4, 4, -1]

?

How does this vibe with your suggestion that every Temperament object have a
prime limit, and also that there be just one Temperament class for all
ranks? JS supports multidimensional arrays, but I can't give them properties
like limit.

Maybe the bsest solution is to use Javascript Object Notation, or JSON. Then
we can be like

var tet12 = {limit: 5, val: [12, 19, 28]};
var syntonic = {monzo: [-4, 4, -1]}

So that might be a decent start.

Mike

🔗Graham Breed <gbreed@...>

9/7/2011 5:25:29 AM

Mike Battaglia <battaglia01@...> wrote:
> On Sep 7, 2011, at 7:28 AM, Graham Breed
> <gbreed@...> wrote:
>
> Mike Battaglia <battaglia01@...> wrote:
>
> > That's what I really like, but since JS doesn't let you
> > overload operators and define new literals, not much of
> > an option. Maybe a suitable compromise would be
> > something like
> >
> > var tet12 = V([12, 19, 28])
> > var syntonic = M([-4, 4, -1])
>
> How about
>
> var tet12 = [12, 19, 28]
> var syntonic = [-4, 4, -1]
>
> ?
>
> How does this vibe with your suggestion that every
> Temperament object have a prime limit, and also that
> there be just one Temperament class for all ranks? JS
> supports multidimensional arrays, but I can't give them
> properties like limit.

If you want tet12 to actually be a temperament, you write

tet12 = Temperament([[12,19,28]], plimit(5))

or maybe

tet12 = Temperament([[12,19,28]], consecutivePrimes)

or even

tet12 = Temperament([[12,19,28]])

where you know to default to consecutive primes as a
default argument. (I don't know how this works with
JavaScript.) You can also say

tet12 = EqualTemperament([12,19,28], plimit(5))

where EqualTemperament could return a Temperament or a
subclass of Temperament. The great thing is that you don't
need to decide right now. (Unless JavaScript requires the
"new" keyword, in which case it has to be a factory
function either way.)

There are also things like

var tet12 = BestEqualTemperament(12, plimit(5))

or

var tet12 = BestTemperament([12], plimit(5))

or

var tet12 = NamedTemperament("12p", plimit(5))

or

var tet12 = BestTemperament(plimit(5), 12)

where you could extend this to higher ranks by adding more
arguments. It's important to put the prime limit first in
this case. So the decision you have to make is whether to
have the prime limit as the first, compulsory argument or
second, optional argument with the mapping specified as an
array.

For the dual approach, maybe

var meantone = Temperament(UnisonVectors([[-4,4,-1]]))

or, more explicitly

var meantone = Temperament(
UnisonVectors([[-4,4,-1]]).mapping(),
plimit(5))

The difficult part is getting the mapping() method to
work, not worrying about the syntax.

You may want to replace plimit() (a function that returns
an array) with Plimit (a class that includes the sizes of
each prime and strings to represent them). You could even
make the different temperament factories methods of Plimit,
I suppose.

> Maybe the bsest solution is to use Javascript Object
> Notation, or JSON. Then we can be like
>
> var tet12 = {limit: 5, val: [12, 19, 28]};
> var syntonic = {monzo: [-4, 4, -1]}
>
> So that might be a decent start.

Why, what problem does it solve?

Graham

🔗Mike Battaglia <battaglia01@...>

9/8/2011 12:27:36 AM

I've liked your ideas so far and agree that too much encapsulation is
a bad thing. I think JSON will make this a lot simpler, and I'll
explain below.

On Wed, Sep 7, 2011 at 8:25 AM, Graham Breed <gbreed@...> wrote:
>
> The difficult part is getting the mapping() method to
> work, not worrying about the syntax.

I think a dot product function is easy, I'm just OCD about this stuff :)

> > Maybe the bsest solution is to use Javascript Object
> > Notation, or JSON. Then we can be like
> >
> > var tet12 = {limit: 5, val: [12, 19, 28]};
> > var syntonic = {monzo: [-4, 4, -1]}
> >
> > So that might be a decent start.
>
> Why, what problem does it solve?

Well, for starters, vals and monzos are dual to one another. This
approach will enable us to have just one type of object, which could
represent a val or set of vals, a monzo or set of monzos, etc, by
simply autocalculating the vals when you enter in the monzo and prime
limit, or by autocalculating the monzos when you enter a val and prime
limit, etc. So we can do this

var syntonic = {monzo: [-4, 4, -1]} // automatically puts in a limit
of [2, 3, 5]
alert(syntonic.val) //spits out [[1, 1, 0], [0, 1, 4]]
alert(syntonic.rank) //spits out 1
alert(syntonic.monzo) //spits out [-4, 4, -1]
alert(syntonic.limit) //spits out [2, 3, 5]
alert(syntonic.POTE) //spits out 697 or whatever it is

But we could also have done this:

var meantone = {val: [[1, 1, 0], [0, 1, 4]]} // automatically puts in
a limit of [2, 3, 5]
alert(meantone.val) // spits out [[1, 1, 0], [0, 1, 4]]
alert(meantone.rank) //spits out 1
alert(meantone.monzo) //spits out [-4, 4, -1]
alert(meantone.limit) //spits out [2, 3, 5]
alert(meantone.POTE) //spits out 697 or whatever it is

And then we can also do this with the same exact code:

var tet12 = {val: [12, 19, 28], limit: 5}
alert(tet12.val) //spits out [12, 19, 28]
alert(tet12.monzo) //spits out [[-4, 4, -1], [7, 0, -3]]
alert(tet12.limit) // spits out [2, 3, 5]
alert(tet12.POTE) // spits out 98 cents or whatever it is

And then, if we want to deal with subgroups, we can do this

var supra = {comma: [64, 63], limit: [2, 3, 7]}
alert(supra.val) //spits out [[1, 2, 2], [0, -1, 2]], keeping in mind
this is the 2.3.7 subgroup
alert(supra.monzo) //spits out [[5, -2, -1]]
alert(supra.limit) //spits out [2, 3, 7]
alert(supra.POTE) //I don't feel like looking this up

We can even do shorthand things like

var tet22 = {val: [22], limit: 5} //autogenerates the 22-tet patent val
var diaschismic = tet22.wedge(tet12)

And now we have a diaschismic object and can do whatever we want with it.

I think that this is a pretty ideal setup and I'm rather happy with
it. It's object oriented, yes, but there's only one object, and why
should there be any others, given the nature of what we're working on?

-Mike

🔗Mike Battaglia <battaglia01@...>

9/8/2011 1:54:16 AM

On Thu, Sep 8, 2011 at 3:27 AM, Mike Battaglia <battaglia01@...> wrote:
>
> var syntonic = {monzo: [-4, 4, -1]} // automatically puts in a limit
> of [2, 3, 5]
> alert(syntonic.val) //spits out [[1, 1, 0], [0, 1, 4]]
> alert(syntonic.rank) //spits out 1
> alert(syntonic.monzo) //spits out [-4, 4, -1]
> alert(syntonic.limit) //spits out [2, 3, 5]
> alert(syntonic.POTE) //spits out 697 or whatever it is

I omitted this from the last message because I wanted to keep it
shorter, but I predict I'll get a wave of people pointing out the
obvious, so I might as well address it first. Yes, I've simplified
things a bit. Obviously just creating an object with "comma" and
"limit" members won't automatically create "monzo" and "val" members
as well. There are a few ways to get the desired behavior, all of
which aren't mutually exclusive

1) Do some fun stuff overriding Object.prototype, although I'm worried
about the legacy compatibility of this, so more ideally
2) Just create a factory method similar to jQuery's $() function.
Let's say T(), for temperament and tuning
3) Use accessor functions, which will be a necessity no matter what we do
4) If {} object literals aren't cross-platform, then just create
val(), monzo(), etc functions, the syntax of which will be made clear
below

I think #2 and #3 will be what happens. So the syntax will be like this:

//T() is a factory method that autogenerates all of the internal parameters
var supra = T({
comma: [64, 63],
limit: [2, 3, 7]
})
alert(supra.getVal());

or, alternatively, on one line

var supra = T({comma: [64, 63], limit: [2, 3, 7]})

and with the accessor functions

alert(supra.getVal());
supra.setLimit([2, 3, 5]);
supra.setComma([[81, 80], [25, 24]]); //note I specified two commas here
alert(supra.getVal()); //should say [7, 11, 16]

This is great because it gives you the syntax of Objective-C in
Javascript, and won't require that you memorize which parameters go
there. I don't want to do #4 because I think it's less elegant, but if
legacy browser compatibility turns out to be an issue with the {}
literals, we have this as a fallback:

var supra = T(
comma([64, 63]),
limit([2, 3, 7])
)

I don't want to do this though because it'll be more tedious to code
up. But there you go.

-Mike

🔗Graham Breed <gbreed@...>

9/8/2011 12:12:18 PM

Mike Battaglia <battaglia01@...> wrote:

> On Wed, Sep 7, 2011 at 8:25 AM, Graham Breed
> <gbreed@...> wrote:
> >
> > The difficult part is getting the mapping() method to
> > work, not worrying about the syntax.
>
> I think a dot product function is easy, I'm just OCD
> about this stuff :)

Converting a set of unison vectors to a mapping isn't about
a dot product. It means finding a null space basis, and
saturating it. How difficult that is depends on what
library you're going to use, and you haven't enlightened us
about that yet.

> > > Maybe the bsest solution is to use Javascript Object
> > > Notation, or JSON. Then we can be like
> > >
> > > var tet12 = {limit: 5, val: [12, 19, 28]};
> > > var syntonic = {monzo: [-4, 4, -1]}
> > >
> > > So that might be a decent start.
> >
> > Why, what problem does it solve?
>
> Well, for starters, vals and monzos are dual to one
> another. This approach will enable us to have just one
> type of object, which could represent a val or set of
> vals, a monzo or set of monzos, etc, by simply
> autocalculating the vals when you enter in the monzo and
> prime limit, or by autocalculating the monzos when you
> enter a val and prime limit, etc. So we can do this

If you want one object to represent all these different
things, doesn't an array fit the bill? Where they're
different, they should be different objects.

> var syntonic = {monzo: [-4, 4, -1]} // automatically puts
> in a limit of [2, 3, 5]
> alert(syntonic.val) //spits out [[1, 1, 0], [0, 1, 4]]
> alert(syntonic.rank) //spits out 1
> alert(syntonic.monzo) //spits out [-4, 4, -1]
> alert(syntonic.limit) //spits out [2, 3, 5]
> alert(syntonic.POTE) //spits out 697 or whatever it is

How does this work? You're assuming something from JSON I
haven't seen. What was wrong with JavaScript objects
anyway? After that, what you need is a temperament object
with a magic constructor.

<snip>
> And then we can also do this with the same exact code:
>
> var tet12 = {val: [12, 19, 28], limit: 5}
> alert(tet12.val) //spits out [12, 19, 28]
<snip>

If this mysterious JSON implementation allows different
constructor signatures, yes.

> I think that this is a pretty ideal setup and I'm rather
> happy with it. It's object oriented, yes, but there's
> only one object, and why should there be any others,
> given the nature of what we're working on?

There shouldn't be any other classes for temperaments.
There should be a different object for each temperament you
look at, depending on what you think a temperament is.
That's what your message seems to be about: let's have a
class for temperaments (a good idea, I already do that in
Python) and let's use a strange syntax, for some reason.

From the other message: why are you defining these
dictionaries instead of using the normal function and method
signatures? I can't find my JavaScript reference so I'm
not sure what the restrictions are. But it's best write
the language the way it's supposed to be written instead of
trying to work around it, especially while you're building
up your experience. In Python, you're only allowed one
constructor, so the idiomatic way is to have a canonical
constructor taking parameters that match the internal state
and factory functions to help you with simpler signatures.
I find that works very well because it makes the interface
clear and takes complexity out of the constructor.

It's great that your examples magically include a "factory
method that autogenerates all of the internal parameters".
In practice, that method's going to be a complete mess and
take up a large part of the implementation.

You talk about getting the syntax of Objective C in
JavaScript. I'm not sure you are, but anyway it's a bad
idea. JavaScript is perfectly capable (if a little
idiosyncratic) as an object oriented language of the C++
family.

As a style point: terse, let alone single letter method
names are usually a bad idea. It's explained, with data,
in Code Complete.

Graham

🔗Mike Battaglia <battaglia01@...>

9/9/2011 2:04:14 AM

On Thu, Sep 8, 2011 at 3:12 PM, Graham Breed <gbreed@...> wrote:
>
> Converting a set of unison vectors to a mapping isn't about
> a dot product. It means finding a null space basis, and
> saturating it. How difficult that is depends on what
> library you're going to use, and you haven't enlightened us
> about that yet.

Oh, I thought you meant mapping() as applying a homomorphism or
something like that.

> If this mysterious JSON implementation allows different
> constructor signatures, yes.

I hope these points are now addressed that you've seen the message
after, so I'll skip them. I was going to try messing around with
Object.prototype to provide the magic constructor, but I'm not sure
how I thought it was going to work at 5 AM when I typed that and an
explicit T(...) or Temperament(...) constructor/factory method will be
a lot better. I keep realizing that there's a very blurry distinction
between constructors and factory methods in JS.

> That's what your message seems to be about: let's have a
> class for temperaments (a good idea, I already do that in
> Python) and let's use a strange syntax, for some reason.
//
> From the other message: why are you defining these
> dictionaries instead of using the normal function and method
> signatures? I can't find my JavaScript reference so I'm
> not sure what the restrictions are.

I'm using the strange syntax as a way of getting around the rough
edges of the normal JS syntax.

In this case, there's more than one way to create a Temperament, and
JavaScript doesn't allow you to define a few different overridden
constructors like you'd hope. So I'd have to have to have factory
methods for Monzo/Val/Wedgie/Comma, and then one canonical
super-constructor that's called by all of those.

That wouldn't be too bad, and it'd look something like this:

function Temperament(val, monzo, wedgie, comma, limit);

function Monzo(m, plimit) {
return new Temperament(null, m, null, null, plimit);
}

function Val(v, plimit) {
return new Temperament(v, null, null, null, plimit);
}

var syntonic = Monzo([-4, 4, -1], 5) //returns a Temperament object
var meantone = Val([[1, 1, 0], [0, 1, 4]], 5)

The reason I suggested doing JSON is that the syntax is going to suck
when we do something like this:

var RandomSubgroupTemperament = Val([[4, 3, 2, 6], [1, 1, 0, 7], [9,
0, 19, 5], [6, 7, 5, 4]], [2, 3, 5, 11])

If you grab your reading glasses and an electron microscope, you'll
see that this function actually takes two parameters - the first is a
4x4 multidimensional array, and the second is a 1x4 array that
specifies the prime limit. This is going to make the code a nightmare
to read. I feel it's rather easily resolved by just passing an object
where you explicitly type the names in, aka Temperament({val: [[4, 3,
2, 6], [1, 1, 0, 7], [9, 0, 19, 5], [6, 7, 5, 4]], limit: [2, 3, 5,
11]}). Or you can lay it out on more than one line, like

var RandomSubgroupTemperament = Temperament({
val: [[4, 3, 2, 6],
[1, 1, 0, 7],
[9, 0, 19, 5],
[6, 7, 5, 4]],
limit: [2, 3, 5, 11]
})

The tradeoff is that you have to remember to put {} in (), leading to
the ({ .... }) syntax for Temperament. This isn't uncommon in modern
JS programming, as I'll address below. And at any rate, no, it isn't
perfect, but I think it's much better than
[3,24[,[24[2,[,42[24,[2,42,[42,, which is what the other one looked
like.

Of course, since things in {.......} are objects, you could also do it
like this:

var params = new Object;
params["val"] = [[4, 3, 2, 6], [1, 1, 0, 7], [9, 0, 19, 5], [6, 7, 5, 4]];
params["limit"] = [2, 3, 5, 11];
var RandomSubgroupTemperament = Temperament(params)

That's just more typing so I don't think it'll happen too much.

If you grab your reading glasses and an electron microscope, you'll
see that this function actually takes two parameters - the first is a
4x4 multidimensional array, and the second is a 1x4 array that
specifies the prime limit. It would be better if you could do
something like Val([[1 0 0 0][0 1 0 0][0 0 1 0][0 0 0 1]], [2 3 5
11]), although I don't even like that, but anyway it's off the table.
This is a particularly irritating fact in the JS syntax, but

It would be better if you could do something like Val([[1 0 0 0][0 1 0
0][0 0 1 0][0 0 0 1]], [2 3 5 11]), although I don't even like that,
but anyway it's off the table. This is a particularly irritating fact
in the JS syntax, but I feel it's rather easily resolved by just
passing an object where you explicitly type the names in, aka
Temperament({val: [........], limit: [........]}).

One solution is just to do it both ways, e.g. like this:

function Temperament(JSONobject) {
... //actually makes the Temperament by using the JSON notation I
laid out before
}

function Monzo(m, plimit) {
return new Temperament({monzo: m, limit: plimit});
}

function Val(v, plimit) {
return new Temperament({val: v, limit: plimit});
}

var syntonic = Monzo([-4, 4, -1], 5) //returns a Temperament object
var alsosyntonic = Temperament({monzo: [-4, 4, -1], limit:5}) //this
is the same as the above, in fact Monzo() simply calls this explicitly

var meantone = Val([[1, 1, 0], [0, 1, 4]], 5)
var alsomeantone = Temperament({val: [[1, 1, 0], [0,1, 4]], limit:5})

> But it's best write
> the language the way it's supposed to be written instead of
> trying to work around it, especially while you're building
> up your experience.

The problem is that JS programming conventions have evolved somewhat
since way back a decade ago when I started programming in it. I didn't
even know that {} object literals existed back then, but they're all
the rage nowadays.

The T(...) method in my original post should have been
Temperament(...), and I mistakenly called it a "factory method," but
it's probably better thought of as a constructor in which you just
don't use the "new" keyword. The distinction is slightly blurred in a
weakly-typed language like Javascript that doesn't really have
classes. But I used the T(...) syntax as a nod to Prototype's and
jQuery's $() function, which leads to syntax like this (taken from the
jQuery FAQ)

$("div").click(function(){
if ( $(this).hasClass("protected") )
$(this)
.animate({ left: -10 })
.animate({ left: 10 })
.animate({ left: -10 })
.animate({ left: 10 })
.animate({ left: 0 });
});

This code makes it so that any time you click on a DIV element, it
moves back and forth 10 pixels at a time. I just realized that they're
passing objects in methods as well - look at animate({left: -10})
above, although this function takes an object as only its first
parameter (specifying the CSS settings you're animating towards), with
three other normal parameters after. But at any rate, I guess I'm not
the only person to resolve JS's silly constructor overriding issues
like this.

However, despite that method-chaining, object-passing, and $trange
$yntax is all the rage these days in our brave new Web 2.9 world, it
might be more intuitive for a lot of us to deal with something closer
to standard C-syntax.

> As a style point: terse, let alone single letter method
> names are usually a bad idea. It's explained, with data,
> in Code Complete.

I'm not sure what "terse" means in this context, but I'm pretty sure
Code Complete won't like $ in Prototype and jQuery then, since $ is
both an object (like $.each(...)) and a function (like
$("#anchor").animate(....)). But to me, these conventions are a
godsend, and go a long way in making the sometimes idiosyncrasies of
JS coding far more palatable.

-Mike

🔗Mike Battaglia <battaglia01@...>

9/9/2011 4:09:13 AM

On Fri, Sep 9, 2011 at 5:04 AM, Mike Battaglia <battaglia01@...> wrote:
>
> If you grab your reading glasses and an electron microscope, you'll
> see that this function actually takes two parameters - the first is a
> 4x4 multidimensional array, and the second is a 1x4 array that
> specifies the prime limit. It would be better if you could do
> something like Val([[1 0 0 0][0 1 0 0][0 0 1 0][0 0 0 1]], [2 3 5
> 11]), although I don't even like that, but anyway it's off the table.
> This is a particularly irritating fact in the JS syntax, but

If you grab your reading glasses and an electron microscope, you'll
see that I typed this paragraph like three times. Looks like I didn't
even both to finish the paragraph this time.

-Mike

🔗Graham Breed <gbreed@...>

9/9/2011 2:21:19 PM

Mike Battaglia <battaglia01@...> wrote:

> In this case, there's more than one way to create a
> Temperament, and JavaScript doesn't allow you to define a
> few different overridden constructors like you'd hope. So
> I'd have to have to have factory methods for
> Monzo/Val/Wedgie/Comma, and then one canonical
> super-constructor that's called by all of those.

Factory functions are what you want, yes. Maybe factory
methods if PrimeLimit is the object they're methods of.

> That wouldn't be too bad, and it'd look something like
> this:
>
> function Temperament(val, monzo, wedgie, comma, limit);

That's a factory function. Why do you need a wedgie?
What's the difference between a monzo and a comma?

> function Monzo(m, plimit) {
> return new Temperament(null, m, null, null, plimit);
> }
>
> function Val(v, plimit) {
> return new Temperament(v, null, null, null, plimit);
> }

You shouldn't call them Val or Monzo unless they return
Vals or Monzos. If you're getting a temperament from
monzos, the function should be TemperamentFromMonzos. And
calling the Temperament constructor with that many nulls is
asking for trouble. You should calculate the other
parameters in the factories.

You're going to need chained if statements, or maybe a
switch, to decide how to fill in the blanks if you want
random things to be null. Object oriented design is
supposed to avoid that. By one rule of thumb, object
orientated programing is about not using switch the same
way procedural programming is about not using goto.

> var syntonic = Monzo([-4, 4, -1], 5) //returns a
> Temperament object var meantone = Val([[1, 1, 0], [0, 1,
> 4]], 5)
>
> The reason I suggested doing JSON is that the syntax is
> going to suck when we do something like this:
>
> var RandomSubgroupTemperament = Val([[4, 3, 2, 6], [1, 1,
> 0, 7], [9, 0, 19, 5], [6, 7, 5, 4]], [2, 3, 5, 11])

Yes, so you should say

var randomMapping = [[4, 3, 2, 6],
[1, 1,0, 7],
[9, 0, 19, 5],
[6, 7, 5, 4]]
var limit11no7 = [2, 3, 5, 11]

var random = TemperamentFromMapping(
randomMapping, limit11no7)

> If you grab your reading glasses and an electron
> microscope, you'll see that this function actually takes
> two parameters - the first is a 4x4 multidimensional
> array, and the second is a 1x4 array that specifies the
> prime limit. This is going to make the code a nightmare
> to read. I feel it's rather easily resolved by just
> passing an object where you explicitly type the names in,
> aka Temperament({val: [[4, 3, 2, 6], [1, 1, 0, 7], [9, 0,
> 19, 5], [6, 7, 5, 4]], limit: [2, 3, 5, 11]}). Or you can
> lay it out on more than one line, like

You should give names to all your constants according to
the general programming rule. Code that's full of magic
numbers is generally unreadable.

> var RandomSubgroupTemperament = Temperament({
> val: [[4, 3, 2, 6],
> [1, 1, 0, 7],
> [9, 0, 19, 5],
> [6, 7, 5, 4]],
> limit: [2, 3, 5, 11]
> })

That's not much better than

var RandomSubgroupTemperament = Temperament(
[[4, 3, 2, 6],
[1, 1, 0, 7],
[9, 0, 19, 5],
[6, 7, 5, 4]],
[2, 3, 5, 11]
)

which avoids calling something a val when it isn't a val.

> The problem is that JS programming conventions have
> evolved somewhat since way back a decade ago when I
> started programming in it. I didn't even know that {}
> object literals existed back then, but they're all the
> rage nowadays.

Right, I was out of date as well. The curly brackets
define an object, that may include methods. You can
promote that object to have the standard methods for a
class. And it's the standard way to implement named
parameters. No need to bring in JSON, which is for passing
data between applications.

> The T(...) method in my original post should have been
> Temperament(...), and I mistakenly called it a "factory
> method," but it's probably better thought of as a
> constructor in which you just don't use the "new"
> keyword. The distinction is slightly blurred in a
> weakly-typed language like Javascript that doesn't really
> have classes. But I used the T(...) syntax as a nod to
> Prototype's and jQuery's $() function, which leads to
> syntax like this (taken from the jQuery FAQ)

It's a factory. That's exactly what a factory does:
construct an object and return it.

> > As a style point: terse, let alone single letter method
> > names are usually a bad idea. It's explained, with data,
> > in Code Complete.
>
> I'm not sure what "terse" means in this context, but I'm
> pretty sure Code Complete won't like $ in Prototype and
> jQuery then, since $ is both an object (like $.each(...))
> and a function (like $("#anchor").animate(....)). But to
> me, these conventions are a godsend, and go a long way in
> making the sometimes idiosyncrasies of JS coding far more
> palatable.

Terse means short. I don't know where the $ came from.
Short variable names are allowable if they're only used for
a small piece of code. Then you don't have to hunt around
to see what they mean. But I think McConnell (who wrote
Code Complete) would still disapprove. I don't have the
book to check.

There's nothing wrong with an object being callable.

Graham

🔗Mike Battaglia <battaglia01@...>

9/10/2011 1:50:07 AM

On Fri, Sep 9, 2011 at 5:21 PM, Graham Breed <gbreed@...> wrote:
>
> You shouldn't call them Val or Monzo unless they return
> Vals or Monzos. If you're getting a temperament from
> monzos, the function should be TemperamentFromMonzos. And
> calling the Temperament constructor with that many nulls is
> asking for trouble. You should calculate the other
> parameters in the factories.

Much of your reply revolves around the central question is whether the
Temperament(...) constructor should be a constructor that anyone ever
calls directly. If so, then having it take a single object as a
parameter is a good way to do it, at least if you want it to be
constructible in more than one way (by Monzo, by Val, etc).

You seem to be advocating that no, people should just call the factory
functions, as that's the most OOP-oriented solution after constructor
overriding, which JS doesn't allow. This is another perfectly good
paradigm, and it makes more sense from an OOP standpoint. It again
boils down to differing expectations between C++/OOP developers and
developers in modern scripting languages like JS, and differing views
on what paradigm is best to use when developing a program.

By now it should be clear that OOP in JavaScript is more or less a
hack job. As you saw yourself, there's always some kind of silly under
the hood tweak you need to get some basic feature of OOP to work.
Javascript is full of stuff like that. There don't really exist
private variables, nor do there really exist constructors, but by
doing clever things with local variables inside a function and using
the "this" keyword, you can create the illusion that there are.

Hacks like these then become standard, because people are always
trying to expand the scope of what the web can do, but they're
syntactically messy and make it difficult for programmers to read one
another's code. What I've seen happen in the last half-decade or so is
that industry leaders (like Google) are coming up with libraries to
wrap the hacks (like jQuery), and in many cases innovating
syntactically so as to give developers some consistency. Many of these
innovations are "weird" at first, but often make things way easier in
the long run. JSON is a great example, which damn near elevates the
object literal syntax to fine art; I doubt anyone was seriously using
either the {} syntax or using objects like that at all 10 years ago.
These innovations draw programming conventions in bold new directions,
sometimes appearing strange to a C/C++ or Java programmer, but I
believe they've overall made JavaScript much easier to program in.

A great example that's relevant to all this is the document object
model and how it relates to Temperament object factory functions. The
JS DOM has functions like document.getElementById(),
document.getElementsByTagName(), document.getElementsByClassName(),
etc, each of which return an Array of HTML elements that match what
you put in. This is much like how you suggested having different
functions like temperamentFromMapping(), temperamentFromMonzos(),
temperamentFromWedgie(), etc, which from a procedural standpoint makes
sense when you can't override a constructor and create separate
Mapping objects.

I and many other programmers have found the DOM to be an unbelievable
pain in the ass to deal with, and so libraries like jQuery and
Prototype responded with the $(...) function, which takes a string
representing a CSS selector that handles all of those. This is much
like my generalized Temperament() of T() function which handles all of
your suggested factory methods. In case that's not clear, $("DIV") is
the same as document.getElementsByTagName("DIV"), and $(".foo") is the
same as document.getElementsByClassName("foo").

You'll really hate the way jQuery codes up event handlers. If you want
to add an onclick event handler to all div with classnames of foo, you
do it like this, passing an anonymous function to the click(...)
method:

$('div.foo').click(function() {
$('span', this).addClass('bar');
});

To you I assume that this is infinitely weirder than the syntax I
suggested, but for web development it's a godsend. It makes things
easy to do, and is quick to type, easy to understand, gives everyone
just one syntax to use, is still more or less C-syntax, and is in
general such a useful paradigm that I don't ever want to deal with
getElementById() ever again. So likewise, I think a I'd use a function
like Temperament({val: [...], monzo: [...]}) more than having separate
getTemperamentFromMonzos() and such functions, specifically because of
my experience with how jQuery's improved the JavaScript DOM.

In short, the development of JS in the last 10 years has been a series
of realizations that people are being limited by the usual C-style way
of thinking. If JS lets you do dumb things like passing an object
literal containing an anonymous function into another anonymous
function, then you might as well develop new syntactic conventions
around those features if they let us do things that we can't already
do. I've found it really interesting to watch the whole thing, because
it's taught me how transient programming conventions can be, and how
what's really important is clarity of thinking and communication.

But in this case, you may not like it. OK, maybe we can meet somewhere
in the middle. What I could do is make factory functions for Monzo,
Val, Wedgie, Comma, and whatever else, and then also make a factory
function - NOT a constructor - called Tuning. Tuning takes an object
as described above, simply figures out what type of data you've passed
in, and calls either Monzo, Val, Wedgie, or Comma, where a Temperament
object is created, or something like that.

I think that Tuning is a good because it doesn't just have to refer to
a particular temperament or tuning, but could also more generally
pertain to objects that are related in some way to tuning, such as
intervals. But then, underneath the hood, your Tuning function
actually creates a Temperament object, signifying that all of these
things are related and dual to one another in the theory of regular
Temperament.

So Tuning is the class, and you have Monzo, Val, Wedgie, Comma, etc
constructors. And then, for those who are more into modern
Prototype/jQuery syntax, we have the master Tuning function, which
does nothing but call one of the above. Maybe that would make everyone
happy.

-Mike

🔗genewardsmith <genewardsmith@...>

9/10/2011 8:30:27 AM

--- In tuning@yahoogroups.com, Mike Battaglia <battaglia01@...> wrote:

> What I could do is make factory functions for Monzo,
> Val, Wedgie, Comma, and whatever else, and then also make a factory
> function - NOT a constructor - called Tuning. Tuning takes an object
> as described above, simply figures out what type of data you've passed
> in, and calls either Monzo, Val, Wedgie, or Comma, where a Temperament
> object is created, or something like that.

What might be relevant to this conversation are the various forms of denoting abstract regular temperaments and the notes thereof, to which a specific tuning could be applied:

http://xenharmonic.wikispaces.com/Abstract+regular+temperament