Module: Musa::Series::Serie::Prototyping Private

Defined in:
lib/musa-dsl/series/base-series.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Prototype/instance state management for Series.

Implements the prototype/instance pattern enabling reusable serie definitions. Every serie exists in one of three states:

States

  • :prototype - Template state, cannot consume, can create instances
  • :instance - Active state, can consume values, has independent state
  • :undefined - Unresolved state, dependencies not yet resolved

State Queries

serie.state        # => :prototype | :instance | :undefined
serie.prototype?   # => true if prototype
serie.instance?    # => true if instance
serie.undefined?   # => true if undefined

State Transitions

Prototype Creation

Created by constructors (S, E, RND, etc.):

proto = S(1, 2, 3)
proto.prototype?  # => true

Instance Creation

Via .instance (or .i alias):

inst = proto.instance
inst.instance?  # => true

State Resolution

Undefined series resolve state from sources:

proxy = PROXY()  # Undefined
proxy.source = S(1, 2, 3)  # Becomes prototype

Cloning Behavior

Creating instance clones the serie and all dependencies:

proto = S(1, 2, 3).map { |x| x * 2 }
inst = proto.instance  # Clones both map and S

Validation

Operations check state permissions:

  • next_value, restart require :instance
  • infinite?, to_a allow :prototype
  • Undefined state raises PrototypingError

Musical Applications

  • Reusable melodic patterns
  • Multiple independent playbacks
  • Lazy definition of transformations
  • Memory-efficient pattern libraries

Defined Under Namespace

Classes: PrototypingError

Instance Method Summary collapse

Instance Method Details

#defined?Boolean

Checks if serie state is defined (not undefined).

Returns:

  • (Boolean)

    true if prototype or instance, false if undefined



361
362
363
# File 'lib/musa-dsl/series/base-series.rb', line 361

def defined?
  !undefined?
end

#instance(built = nil) ⇒ Serie Also known as: i

Creates or returns instance of serie.

  • If already instance, returns self
  • If prototype, creates new instance by cloning
  • If undefined, raises PrototypingError

Cloning Process

  1. Clones serie structure
  2. Marks clone as :instance
  3. Propagates instance creation to sources
  4. Calls init if defined

Each call creates independent instance with separate state.

Examples:

Create instances

proto = S(1, 2, 3)
a = proto.instance
b = proto.instance  # Different instance

a.next_value  # => 1
b.next_value  # => 1 (independent)

Returns:

  • (Serie)

    instance serie

Raises:



431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/musa-dsl/series/base-series.rb', line 431

def instance(built = nil)
  try_to_resolve_undefined_state_if_needed

  if instance?
    self
  elsif prototype?
    # A prototype reached again while it is STILL BEING instantiated
    # gives back the instance being built for it. That is a cycle
    # closing, and without this the walk went round the ostinato PROXY
    # exists for cloning for ever; with it, the cycle becomes a cycle
    # again, of the same shape, between the new nodes (issue #77).
    #
    # IN PROGRESS, not merely seen: the register is a recursion stack,
    # and the entry goes away when the instantiation finishes. A
    # prototype that appears twice in an ACYCLIC graph is two separate
    # uses of the same template and must give two independent instances
    # -- `H(a: fib, b: fib.reverse)` has to iterate `fib` twice, not hand
    # both branches one iterator for them to drain between them.
    # Registering for the whole walk, as this did between 0.46.0 and
    # 0.47.1, gave them the same one.
    built ||= {}
    in_progress = built[object_id]
    return in_progress if in_progress

    new_instance = clone
    built[object_id] = new_instance

    begin
      new_instance._instance!(built)
    ensure
      built.delete(object_id)
    end

    new_instance.mark_as_instance!(self)
    new_instance.init if new_instance.respond_to?(:init)

    new_instance
  else
    raise PrototypingError, 'Can\'t get an instance of an undefined serie'
  end
end

#instance?Boolean

Checks if serie is in instance state.

Returns:

  • (Boolean)

    true if instance, false otherwise



341
342
343
344
# File 'lib/musa-dsl/series/base-series.rb', line 341

def instance?
  try_to_resolve_undefined_state_if_needed
  @state&.==(:instance)
end

#prototypeSerie Also known as: p

Returns prototype of serie.

  • If already prototype, returns self
  • If instance, returns original prototype (if available)
  • If undefined, raises PrototypingError

Examples:

Get prototype

proto = S(1, 2, 3)
inst = proto.instance
inst.prototype  # => proto

Returns:

  • (Serie)

    prototype serie

Raises:



381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/musa-dsl/series/base-series.rb', line 381

def prototype
  try_to_resolve_undefined_state_if_needed

  if prototype?
    self
  elsif instance?
    # if the series has been directly created as an instance (i.e., because is an operation over an instance)
    # the prototype doesn't exist.
    #
    @instance_of
  else
    raise PrototypingError, 'Can\'t get the prototype of an undefined serie'
  end
end

#prototype?Boolean

Checks if serie is in prototype state.

Returns:

  • (Boolean)

    true if prototype, false otherwise



331
332
333
334
# File 'lib/musa-dsl/series/base-series.rb', line 331

def prototype?
  try_to_resolve_undefined_state_if_needed
  @state&.==(:prototype)
end

#stateSymbol

Returns current state of serie.

Attempts to resolve undefined state from sources before returning. State is one of: :prototype, :instance, or :undefined.

Returns:

  • (Symbol)

    current state (:prototype, :instance, :undefined)



321
322
323
324
# File 'lib/musa-dsl/series/base-series.rb', line 321

def state
  try_to_resolve_undefined_state_if_needed
  @state || :undefined
end

#undefined?Boolean

Checks if serie is in undefined state.

Returns:

  • (Boolean)

    true if undefined, false otherwise



351
352
353
354
# File 'lib/musa-dsl/series/base-series.rb', line 351

def undefined?
  try_to_resolve_undefined_state_if_needed
  @state.nil? || @state == :undefined
end