Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

patternified objects #615

Closed
felixroos opened this issue Jun 25, 2023 · 4 comments
Closed

patternified objects #615

felixroos opened this issue Jun 25, 2023 · 4 comments
Labels
feature completely new feature

Comments

@felixroos
Copy link
Collaborator

it would be neat if there was a way to patternify objects, e.g this does not work right now:

s("bd sd").pianoroll({ flipTime: "<0 1>" })

there could be a function patternifyObject that turns an object into a pattern of objects:

patternifyObject({ flipTime: "<0 1>" })

which yields something like

cat({ flipTime: 0 }, {flipTime: 1})

this pattern then could used as the input for pianoroll (assuming it's using register).

I am still unsure how multiple pattern values should work, but probably they are just joined from left to right.
(Objects have no defined order in theory, but in practice they have afaik...)

If this would work, local namespaces would allow to pattern things that are not available in the global scope.
This can be useful for things with alot of params, where the naming just makes sense within the local namespace, think synth params or visual effects.

@felixroos
Copy link
Collaborator Author

related: #223 (comment)

@felixroos felixroos added the feature completely new feature label Jun 26, 2023
@felixroos
Copy link
Collaborator Author

I've thought about using this for voicing: #647 (comment)
In the end, I've decide against it, so instead of

voicing({
  n: run(12),
  chord: "<Dm7 G7 C^7 A7b9>",
  dict: "lefthand"
})

it is now

voicing(
  n(run(12))
  .chord("<Dm7 G7 C^7 A7b9>")
  .dict("lefthand")
)

or alternatively

n(run(12))
.chord("<Dm7 G7 C^7 A7b9>")
.dict("lefthand")
.voicing()

I now think that objects are too limited to support everything tidal/strudel can do + it creates redundancies and confusion in the API.

Why?

1. you cannot do "partial application" / branching:

stack(
  n("5 6 7"),
  offset(-1)
)
.chord("<Am Dm>")
.dict("<ireal ireal-ext>/2")
.voicing()
.piano()

With the hypothetical object syntax, you'd need to duplicate all things that come after the branch:

stack(
  voicing({
    n: "5 6 7",
    chord: "<Am Dm>",
    dict: "<ireal ireal-ext>/2"
  }),
  voicing({
    offset: -1,
    chord: "<Am Dm>"
    dict: "<ireal ireal-ext>/2"
  })
)
.piano()

Of course you could help yourself with variables:

let v = {
  chord: "<Am Dm>"
  dict: "<ireal ireal-ext>/2"
};

stack(
  voicing({
    n: "5 6 7",
    ...v
  }),
  voicing({
    offset: -1,
    ...v
  })
)
.piano()

but that does not look ideal..

2. it's not shorter

compare:

voicing(
  n("5 6 7")
  .chord("<Am Dm>")
  .dict("<ireal ireal-ext>/2")
).s("piano")

against:

voicing({
  n: "5 6 7"
  ,chord: "<Am Dm>"
  ,dict: "<ireal ireal-ext>/2"
}).s("piano")

I've written it with prefixed comma, so it's easier to compare:
When using space after ":", object syntax is actually 2 characters longer in total..

I'd also argue that the former is more readable, as it follows the same style as the rest of the code.

3. there is no solution for operators and aligments

with method syntax, you can use alternative alignments, like set.squeeze:

chord("<Am [Dm Gm]>")
.set.squeeze(n("0 1 2 3"))
.voicing()
.s("piano")

object notation would require extra syntax for that:

voicing({
  chord: "<Am [Dm Gm]>",
  n: set.squeeze("0 1 2 3") // ???
})
.s("piano")

Besides the fact that it might be difficult to implement, I think it could be really confusing to have different ways to do things in "object land".

It's also really easy to find edge cases that are fine in method syntax:

chord("<Am [Dm Gm]>")
.set.squeeze(n("0 1 2 3"))
.add(n("<0 1>"))
.add(n("<0 1>/2"))
.voicing()
.s("piano")
voicing({
  chord: "<Am [Dm Gm]>",
  set.squeeze: n("0 1 2 3"),
  add: n("<0 1>"),
  add: n("<0 1>/2") // not possibel!
}).s("piano")

the fact that object keys are unique is really limiting!

4. conditionals

chord("<Am [Dm Gm]>")
.n("0 1 2 3")
.sometimes(add(n("0,2")))
.sometimes(sub(n("2")))
.voicing()
.s("piano")

again, new syntax would be needed in object land:

voicing({
  chord: "<Am [Dm Gm]>",
  n: "0 1 2 3",
  sometimes: add(n("0,2")) // !?!?
  sometimes: sub(n("2")) // not possible
}).s("piano")

of course, this is not specific to the voicing syntax:

freq("1 2")
.mul(freq("<2 3>"))
.mul(freq(55)) // not possible with objects
.s("piano")

bonus: namespacing

The main selling point of objects seems to be the ability to have shorter properties in a local namespace (#223 (comment)).
But this local namespace can also be achieved with method syntax:

voicing(
  n("0 1 2").chord("Dm")
).log() // logs only { note: .. }

The voicing function will actually "swallow" all properties that are used by it, so n and chord do not appear on the final logged value. This means we can use shorter control names which feel like a local namespace without polluting the rest of the pattern.

conclusion

After going deeper with this idea, I'd now really advise against it, as it is very limited compared to method syntax.

@felixroos
Copy link
Collaborator Author

the extreme form of patternified objects is #257 .. so it seems either go full object notation or full method notation

@felixroos
Copy link
Collaborator Author

closing this for reasons outlined above

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature completely new feature
Projects
None yet
Development

No branches or pull requests

1 participant