how to add some math support to a class

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

how to add some math support to a class

Axel
Hi list,

I'm writing a few simple classes to give me some basic rhythms as arrays
of durs or amps. Now that I'm trying to add a clave that starts with a
rest I'm running into a problem. The Array I'm returning looks like this:

[Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5]

But I would like to be able to scale the durs like this:

[Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale

Since Rest doesn't understand * this fails. So, I've tried to add
support for the * operator to Rest.

+ Rest {
* { arg aRest, adverb;
     ^Rest(this.dur + adverb)
}
}

But Rest(0.5) + 1 still doesn't work. I admit I don't really understand
how these operators are defined as methods. Looking at the
implementation in SimpleNumber hasn't helped. I still don't understand
why e.g.

1 * 2

works like

1.*(2)

although trying to evaluate the latter throws an error.

Could someone please point me to the right direction here?

Cheers,
Axel

_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
search: http://www.listarc.bham.ac.uk/lists/sc-users/search/
Reply | Threaded
Open this post in threaded view
|

Re: how to add some math support to a class

Axel
Ha, two minutes later found something that works. And it's so simple:

+ Rest {
     * { arg adverb; ^Rest(this.dur * adverb)}
}

So I guess that there is some magic built into the language that makes
characters like *,+,- etc work differently.

Nevermind :-)

On 03/06/2017 09:39 PM, Axel wrote:

> Hi list,
>
> I'm writing a few simple classes to give me some basic rhythms as arrays
> of durs or amps. Now that I'm trying to add a clave that starts with a
> rest I'm running into a problem. The Array I'm returning looks like this:
>
> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5]
>
> But I would like to be able to scale the durs like this:
>
> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale
>
> Since Rest doesn't understand * this fails. So, I've tried to add
> support for the * operator to Rest.
>
> + Rest {
> * { arg aRest, adverb;
>     ^Rest(this.dur + adverb)
> }
> }
>
> But Rest(0.5) + 1 still doesn't work. I admit I don't really understand
> how these operators are defined as methods. Looking at the
> implementation in SimpleNumber hasn't helped. I still don't understand
> why e.g.
>
> 1 * 2
>
> works like
>
> 1.*(2)
>
> although trying to evaluate the latter throws an error.
>
> Could someone please point me to the right direction here?
>
> Cheers,
> Axel
>
> _______________________________________________
> sc-users mailing list
>
> info (subscription, etc.):
> http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
>
> archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
> search: http://www.listarc.bham.ac.uk/lists/sc-users/search/

_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
search: http://www.listarc.bham.ac.uk/lists/sc-users/search/
Reply | Threaded
Open this post in threaded view
|

Re: how to add some math support to a class

Nathan Ho
In reply to this post by Axel
On 2017-03-06 12:39, Axel wrote:

> Hi list,
>
> I'm writing a few simple classes to give me some basic rhythms as
> arrays of durs or amps. Now that I'm trying to add a clave that starts
> with a rest I'm running into a problem. The Array I'm returning looks
> like this:
>
> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5]
>
> But I would like to be able to scale the durs like this:
>
> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale
>
> Since Rest doesn't understand * this fails. So, I've tried to add
> support for the * operator to Rest.
>
> + Rest {
> * { arg aRest, adverb;
>     ^Rest(this.dur + adverb)
> }
> }

Hi Axel,

I don't recommend that you extend core classes. It makes code less
portable and increases risk of conflicts. If a Rest:* method is added to
core or another Quark you use, you'll have to go back and manually fix
all your code to sort out the conflict. Doing this kind of thing to the
language is only advisable in live coding, where convenience takes
priority over robustness.

But if you really want to do this, I think this should do it:

+ Rest {
     * { arg aNumber, adverb;
         ^Rest(this.dur.perform('*', aNumber, adverb));
     }
}

> But Rest(0.5) + 1 still doesn't work. I admit I don't really
> understand how these operators are defined as methods. Looking at the
> implementation in SimpleNumber hasn't helped. I still don't understand
> why e.g.
>
> 1 * 2
>
> works like
>
> 1.*(2)
>
> although trying to evaluate the latter throws an error.

Pretty much every action in sclang boils down to messages being sent to
objects. A message name is identified by any string of bytes. Empty
strings and even strings containing non-ASCII bytes are all valid
message names.

The "foo.bar(baz, quux)" and "bar(foo, baz, quux)" syntaxes are the most
common ways to call a message, but they only work if "bar" is a valid
identifier (starts with a lowercase letter, then any number of
digits/letters/underscores). This limitation is syntactic, not semantic.
You can't write 1.*(2) or *(1, 2) because the sclang syntax doesn't let
you. Non-alphanumeric messages are still valid messages, and they can be
sent using Object:perform:

     1.perform('*', 2)

If the message string consists of one or more characters in
!@%&*-+=|<>?/ (ignoring illegal character combinations like // and /*)
and you're sending exactly one argument, then the expression can be
equivalently written as a binary operator:

     1 * 2

Similarly, "x @%&?! y" is a syntactically valid way of sending the
message "@%&?!". There is nothing special about the * token in this
context.

Adverbs are a weird and rather obscure feature of sclang. The adverb
syntax is an extension of the binary operator syntax, which looks like
this:

     x +.foo y

which is equivalent to sending the message '+' with y as the first
argument and the symbol 'foo' as the second argument:

     x.perform('+', y, 'foo')

For the adverb syntax to be valid, I believe the adverb has to be a
valid identifier or a valid integer literal ("x +.-0x42 y" appears to
parse, weirdly enough!). Note that, even though "foo" appears as a bare
word in the syntax example above, no variable named "foo" is involved.
It is taken as a symbol.

The actual purpose of the adverbs is described in the "Adverbs for
Binary Operators" and "J Concepts in SC" helpfiles, so I won't repeat
them here. Like list comprehensions, few people know about them or need
to use them.


Nathan

_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
search: http://www.listarc.bham.ac.uk/lists/sc-users/search/
Reply | Threaded
Open this post in threaded view
|

Re: how to add some math support to a class

ddw_music
In reply to this post by Axel
Axel wrote
I'm writing a few simple classes to give me some basic rhythms as arrays
of durs or amps. Now that I'm trying to add a clave that starts with a
rest I'm running into a problem. The Array I'm returning looks like this:

[Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5]

But I would like to be able to scale the durs like this:

[Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale

Since Rest doesn't understand * this fails.
The current implementation of Rest is such that you can do math on a pattern or stream that involves rests, but not on the rests themselves.

(
var scale = 2;
Pseq([Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale, 1).asStream.all(())
)

^^ Error

(
var scale = 2;
(Pseq([Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5], 1) * scale).asStream.all(())
)

-> [ 1, 1, 2, 1.5, 1.5, 1 ]

Where did the Rest go in the first item? When you use it in an event pattern, the Rest sets an 'isRest' flag in the resulting event and gives the numeric value to the stream -- so the event acts like a rest.

This design will change at some point -- partly because the above limitation is confusing and hard to justify, and partly because of a concrete case that's extremely difficult to support cleanly.[1] At the time of introducing the Rest class, we couldn't agree whether Rest should support math operators or not -- I even vaguely remember some talk of requiring two Rest classes, one for math and one not, which seemed loony to me so I proposed the current approach. But, in hindsight, we should have gone another way.

FWIW I think the optimal way to add math support for class is to make it a subclass of AbstractFunction, and then implement the "compose/perform...Op..." methods for it, e.g.

        composeUnaryOp { arg aSelector;
                ^dur.perform(aSelector)
        }
        composeBinaryOp { arg aSelector, something, adverb;
                ^dur.perform(aSelector, something, adverb);
        }
        reverseComposeBinaryOp ...
        composeNAryOp ...

        // double dispatch for mixed operations
        performBinaryOpOnSimpleNumber ...
        performBinaryOpOnSignal ...
        performBinaryOpOnComplex ...
        performBinaryOpOnSeqColl ...

But, as Nathan points out, doing this to a core class will mean that your code is not portable. At your own risk...

hjh

[1] https://github.com/supercollider/supercollider/pull/2021
Reply | Threaded
Open this post in threaded view
|

Re: how to add some math support to a class

Axel
Thank you James and Nathan! That was very interesting to read. I'll have
to rethink.

Axel

On 03/07/2017 01:40 AM, ddw_music wrote:

> Axel wrote
>> I'm writing a few simple classes to give me some basic rhythms as arrays
>> of durs or amps. Now that I'm trying to add a clave that starts with a
>> rest I'm running into a problem. The Array I'm returning looks like this:
>>
>> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5]
>>
>> But I would like to be able to scale the durs like this:
>>
>> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale
>>
>> Since Rest doesn't understand * this fails.
>
> The current implementation of Rest is such that you can do math on a pattern
> or stream that involves rests, but not on the rests themselves.
>
> (
> var scale = 2;
> Pseq([Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale, 1).asStream.all(())
> )
>
> ^^ Error
>
> (
> var scale = 2;
> (Pseq([Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5], 1) * scale).asStream.all(())
> )
>
> -> [ 1, 1, 2, 1.5, 1.5, 1 ]
>
> Where did the Rest go in the first item? When you use it in an event
> pattern, the Rest sets an 'isRest' flag in the resulting event and gives the
> numeric value to the stream -- so the event acts like a rest.
>
> This design will change at some point -- partly because the above limitation
> is confusing and hard to justify, and partly because of a concrete case
> that's extremely difficult to support cleanly.[1] At the time of introducing
> the Rest class, we couldn't agree whether Rest should support math operators
> or not -- I even vaguely remember some talk of requiring two Rest classes,
> one for math and one not, which seemed loony to me so I proposed the current
> approach. But, in hindsight, we should have gone another way.
>
> FWIW I think the optimal way to add math support for class is to make it a
> subclass of AbstractFunction, and then implement the
> "compose/perform...Op..." methods for it, e.g.
>
> composeUnaryOp { arg aSelector;
> ^dur.perform(aSelector)
> }
> composeBinaryOp { arg aSelector, something, adverb;
> ^dur.perform(aSelector, something, adverb);
> }
> reverseComposeBinaryOp ...
> composeNAryOp ...
>
> // double dispatch for mixed operations
> performBinaryOpOnSimpleNumber ...
> performBinaryOpOnSignal ...
> performBinaryOpOnComplex ...
> performBinaryOpOnSeqColl ...
>
> But, as Nathan points out, doing this to a core class will mean that your
> code is not portable. At your own risk...
>
> hjh
>
> [1] https://github.com/supercollider/supercollider/pull/2021
>
>
>
> --
> View this message in context: http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/how-to-add-some-math-support-to-a-class-tp7631030p7631036.html
> Sent from the SuperCollider Users New (Use this!!!!) mailing list archive at Nabble.com.
>
> _______________________________________________
> sc-users mailing list
>
> info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
> archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
> search: http://www.listarc.bham.ac.uk/lists/sc-users/search/
>

_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
search: http://www.listarc.bham.ac.uk/lists/sc-users/search/
Reply | Threaded
Open this post in threaded view
|

Re: how to add some math support to a class

julian.rohrhuber
There is a class planned as a superclass for Rest, here is the pull request which is still waiting. Maybe it helps you.

https://github.com/supercollider/supercollider/pull/2361
https://github.com/supercollider/supercollider/pull/2361/commits/ecd154f9ea6718cbc55b7ce5a2b730777699102c




> On 07.03.2017, at 08:03, Axel <[hidden email]> wrote:
>
> Thank you James and Nathan! That was very interesting to read. I'll have to rethink.
>
> Axel
>
> On 03/07/2017 01:40 AM, ddw_music wrote:
>> Axel wrote
>>> I'm writing a few simple classes to give me some basic rhythms as arrays
>>> of durs or amps. Now that I'm trying to add a clave that starts with a
>>> rest I'm running into a problem. The Array I'm returning looks like this:
>>>
>>> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5]
>>>
>>> But I would like to be able to scale the durs like this:
>>>
>>> [Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale
>>>
>>> Since Rest doesn't understand * this fails.
>>
>> The current implementation of Rest is such that you can do math on a pattern
>> or stream that involves rests, but not on the rests themselves.
>>
>> (
>> var scale = 2;
>> Pseq([Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5] * scale, 1).asStream.all(())
>> )
>>
>> ^^ Error
>>
>> (
>> var scale = 2;
>> (Pseq([Rest(0.5), 0.5, 1, 0.75, 0.75, 0.5], 1) * scale).asStream.all(())
>> )
>>
>> -> [ 1, 1, 2, 1.5, 1.5, 1 ]
>>
>> Where did the Rest go in the first item? When you use it in an event
>> pattern, the Rest sets an 'isRest' flag in the resulting event and gives the
>> numeric value to the stream -- so the event acts like a rest.
>>
>> This design will change at some point -- partly because the above limitation
>> is confusing and hard to justify, and partly because of a concrete case
>> that's extremely difficult to support cleanly.[1] At the time of introducing
>> the Rest class, we couldn't agree whether Rest should support math operators
>> or not -- I even vaguely remember some talk of requiring two Rest classes,
>> one for math and one not, which seemed loony to me so I proposed the current
>> approach. But, in hindsight, we should have gone another way.
>>
>> FWIW I think the optimal way to add math support for class is to make it a
>> subclass of AbstractFunction, and then implement the
>> "compose/perform...Op..." methods for it, e.g.
>>
>> composeUnaryOp { arg aSelector;
>> ^dur.perform(aSelector)
>> }
>> composeBinaryOp { arg aSelector, something, adverb;
>> ^dur.perform(aSelector, something, adverb);
>> }
>> reverseComposeBinaryOp ...
>> composeNAryOp ...
>>
>> // double dispatch for mixed operations
>> performBinaryOpOnSimpleNumber ...
>> performBinaryOpOnSignal ...
>> performBinaryOpOnComplex ...
>> performBinaryOpOnSeqColl ...
>>
>> But, as Nathan points out, doing this to a core class will mean that your
>> code is not portable. At your own risk...
>>
>> hjh
>>
>> [1] https://github.com/supercollider/supercollider/pull/2021
>>
>>
>>
>> --
>> View this message in context: http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/how-to-add-some-math-support-to-a-class-tp7631030p7631036.html
>> Sent from the SuperCollider Users New (Use this!!!!) mailing list archive at Nabble.com.
>>
>> _______________________________________________
>> sc-users mailing list
>>
>> info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
>> archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
>> search: http://www.listarc.bham.ac.uk/lists/sc-users/search/
>>
>
> _______________________________________________
> sc-users mailing list
>
> info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
> archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
> search: http://www.listarc.bham.ac.uk/lists/sc-users/search/


_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
search: http://www.listarc.bham.ac.uk/lists/sc-users/search/