A generic function and class constrained by a type

The complete source of generics_event_handlers.ek9, compiled by the EK9 compiler. Shown on generics.html.

#!ek9
defines module site.generics.eventHandlers

  defines function

    //Generic function - now open so can be extended once parameterized.
    eventHandler of type T constrain by Shape as open
      -> shape as T
      require shape?
      //Now it is possible access the 'description' method if needs be.
      require shape.description()?

  defines class

    Text
      mainValue as String?

      default private Text() as pure

      Text() as pure
        -> content as String
        mainValue:=? String(content)

      mainValue() as pure
        <- rtn as String: String(mainValue)

      default operator ?

    Shape as abstract
      shapeInfo as String?

      default private Shape() as pure

      Shape() as pure
        -> info as String
        shapeInfo:=? String(info)

      description() as pure
        <- rtn as String: String(shapeInfo)

      override operator ? as pure
        <- rtn as Boolean: shapeInfo?

    Circle is Shape

      private Circle() as pure
        -> info as String
        //Must chain to an accessible initialising super - Shape's no-arg default is private (E07176).
        super(info)

      Circle() as pure
        super("Circle")

      roundness() as pure
        <- rtn as Float: 0.0

    Square is Shape

      private Square() as pure
        -> info as String
        //Must chain to an accessible initialising super - Shape's no-arg default is private (E07176).
        super(info)

      Square() as pure
        super("Square")

  defines program

    ExampleDriver1()
      stdout <- Stdout()
      stdout.println("Driver for generic event handler")
      eventProcessor <- ShapeEventProcessor()

      //This is not allowed because eventHandler is constrained to Shape
      //textHandler <- (eventProcessor) is eventHandler of Text

      //Setup some event handlers and an example
      //Process through 'Shape'
      shapeHandler <- (eventProcessor) is eventHandler of Shape as function
        eventProcessor.processShapeEvent(shape)

      //Now type safe via generics and direct call to specific method
      circleHandler <- (eventProcessor) is eventHandler of Circle as function
        //Note strict typing means method 'roundness' is accessible.
        Stdout().println("Roundness " + $shape.roundness())
        eventProcessor.processCircleEvent(shape)

      //While handler is specific to Square can still call via processShapeEvent
      squareHandler <- (eventProcessor) is eventHandler of Square as function
        eventProcessor.processShapeEvent(shape)

      //make some calls on the event handlers
      shapeHandler(Circle())
      shapeHandler(Square())

      circleHandler(Circle())
      squareHandler(Square())

      //Will not compile - which is correct!
      //circleHandler(Square())
      //squareHandler(Circle())

  defines class
    //non-generic processor
    ShapeEventProcessor
      stdout as Stdout: Stdout()

      processShapeEvent()
        -> shape as Shape
        require shape?
        stdout.println(`Processed shape [${shape.description()}]`)

      processCircleEvent()
        -> circle as Circle
        require circle?
        stdout.println(`Processed circle [${circle.description()} roundness ${$circle.roundness()}]`)

      default operator ?

  defines program

    ExampleDriver2()
      stdout <- Stdout()
      stdout.println("Driver for generic event handler and generic processor")

      eventProcessor as GenericShapeEventProcessor of Shape: GenericShapeEventProcessor()

      shapeHandler <- (eventProcessor) is eventHandler of Shape as function
        eventProcessor.processShapeEvent(shape)

      //make some calls on the event handler
      shapeHandler(Circle())
      shapeHandler(Square())

  defines class

    GenericShapeEventProcessor of type T constrain by Shape
      stdout as Stdout: Stdout()

      default GenericShapeEventProcessor()

      GenericShapeEventProcessor()
        -> shape as T
        require shape?

      processShapeEvent()
        -> shape as T
        require shape?
        stdout.println(`Processed shape [${shape.description()}]`)

      default operator ?

Download generics_event_handlers.ek9

Output

ExampleDriver1

Driver for generic event handler
Processed shape [Circle]
Processed shape [Square]
Roundness 0.0
Processed circle [Circle roundness 0.0]
Processed shape [Square]

ExampleDriver2

Driver for generic event handler and generic processor
Processed shape [Circle]
Processed shape [Square]