Generic - abstract class confusion

I am getting an error

In src\cml\event.cr:67:18

 67 | def initialize(@events : Array(Event))
                     ^------
Error: instance variable '@events' of ChooseEvent must be Array(Event), not Array(ReceiveEvent(String))

with the following code


# Event class
abstract class Event(T)
  abstract def sync : T

end

class SendEvent(T) < Event(T)
  
  def initialize(@channel : Channel(T), @value : T)
  end

  def sync : T
    @channel.send(@value)
    @value
  end
end

# Receive event
class ReceiveEvent(T) < Event(T)
  
  def initialize(@channel : Channel(T))
  end

  def sync : T
    @channel.receive
  end
end



class ChooseEvent(T) < Event(T)
  def initialize(@events : Array(Event(T)))
  end

  def sync : T
    # Create a buffered channel with capacity 1
    result_channel = Channel(T).new

    # Use a synchronized mechanism to ensure only one result
    done = Channel(Nil).new

    # Create a fiber for each event
    @events.map do |event|
      spawn do
        begin
          result = event.sync

          # Try to send the result, but only if no result has been sent yet
          select
          when result_channel.send(result)
            done.send(nil)
          else
            # If send fails (channel is full), do nothing
            nil
          end
        rescue ex
          # Optionally log the exception
          # puts "Event failed: #{ex.message}"
        end
      end
    end

    # Wait for the first result
    select
    when result = result_channel.receive
      result
    when done.receive
      # Synchronization point
      result_channel.receive
    end
  end
end

The example code doesn’t reproduce the issue from what I can tell, unless it’s missing some stuff? But if I had to guess I’d say this is basically Inheritance - Crystal. I.e. it’s expecting an Array(Event) which is an array that can hold any Event instance. However it’s actually getting Array(ReceiveEvent(String)) which means it can only hold ReceiveEvent(String) instances, hence the error.

It does seem to be the problem - How can I correctly initialize the ChooseEvent? I am going to have many types of Event classes - WrapEvent GuardEvent, NackEvent etc -

I think it’s going to be tricky given Event itself is generic. Might be easiest to have another parent type like AbstractEvent or EventBase that isn’t generic such that you can do like [some_event] of AbstractEvent to tell the compiler its an array of any event that is a child of this parent type, regardless of generics.

How can I run the code in Crystal playground and include a link?

Should just be able to paste your code into Carcin, hit Compile & Run button, then copy/paste the URL here.

Thanks @Blacksmoke16 for your quick replies!
Can anyone help to get this code Carcin or something similar to compile?

( I am trying to begin an exploration of concurrent ml ideas - Event abstraction where they the events are lazy - not run until they are synced - right now each type has an explicit sync method but eventually I want to make it implicit and add a correct scheduler implementation - but I am getting stuck at the beginning of my exploration)

Seems to time out, but Carcin compiles at least. Key things I did were:

  1. Make Event(T) extend BaseEvent
  2. Change all the concrete event types to extend Event(T)
  3. Set ChooseEvent initialize to be @events : Array(BaseEvent)
  4. Use of BaseEvent when passing the array to ChooseEvent.new
1 Like

Thank you! I will work on it now that it is compiling