Saturday, December 12, 2020

The Message-Only Architecture

 

OK, I've barely got twenty lines of code into this grand project so far, and it's already time to re-architect!

What I showed last time was the same architecture I have been using since Dinosaurs Roamed the Earth. It works like this: 

  • You define a data structure that represents the problem space.
  • You write a New function for the data structure. 
  • You write a bunch of functions that operate upon that data structure.
  • Each function takes a pointer to the data structure as its first argument.
  • The main program creates the structure, owns it, and operates upon it.
  • You keep getting older until someday you don't.

When I re-re-(re-?)started Tachyon recently, this was the style I automatically used, as a dog returneth to its vomit. Now, before we go too far in this well-trod path I want to recall that I have recently learned a new style and a better, thanks to the fabulous mechanisms for threading and communication of the fabulous new language C-2012, sometimes also called "Go"! Let us take a road less traveled by, for that will make all the difference. Because you've been down there, Neo. You already know that road. You know exactly where it ends. And I know that's not where you want to be. We have come to a fork in the road. Let us take it.

The new style that Go has taught me is this:

  • Instead of a data structure, we have a running goroutine.
  • Instead of fields in a data structure, we have local variables in the running goroutine.
  • There is exactly one function call. It starts the da-    Look at that. I started typing "data structure". Wake up, Neo.      The single function starts the goroutine running, and returns the channel that it is listening to.
  • All further requests that you can make of the goroutine are made through that channel.
  • Sometimes you can pass in your own channel as part of a request. That is your reply-to channel, where the goroutine will send its response to your request.


Here's what it looks like in code:

 (from checkin e39407fd55986f916308a86ebcf11333039cc793 )


Here's the only public function:

func Start_Bulletin_Board ( ) ( Message_Channel ) {
  requests := make ( Message_Channel, 5 )
  go run_bulletin_board ( requests )
  return requests
}

It creates the requests channel, passes that to the goroutine, and passes the channel back to the caller.

Here's the run function:

func run_bulletin_board ( requests Message_Channel ) {
  bb := Bulletin_Board { output_channels : make ( map[string] []Message_Channel, 0 ) }
  for {
    msg := <- requests
    fp ( os.Stdout, "MDEBUG BB |%#v| gets message: |%v|\n", bb, msg )
  }
}


And here it is being used in main() :

  bb_channel := t.Start_Bulletin_Board ( )

  for {
    bb_channel <- msg
    time.Sleep ( 2 * time.Second )
  }


No race conditions in accessing the fields of a structure, in spite of arbitrary extensibility. This goroutine can be used by any number of other routines.

This is a beautiful style and it's going to change everything.

1 comment: