EC Recombination and nested associated types in Rust! Episode 62 of Unhindered by Coding
Channel: Unhindered by Coding!
Date: 19/01/2023

The first stream after the lengthy winter break! I was thinking we were going to do benchmarking work on the Rust EC code, but we ended up doing some more cool generalization, working the recombination tools, and having fun with some nested associated types.

A bunch of new folks came today because of a "manual" raid from another stream from MrHalzy@Twitch. I didn't really know what was going on, and probably didn't respond as well as I could have. I've learned a little about raiding since, and made it easier for folks to actually use the "raiding" feature in Twitch, and I'll try to be a little more thoughtful in the future if folks show up from a raid I should also consider sending my viewers off to someone else at the end of my sessions. I think I got several new visitors from the raid, though, which was very cool!

I had a pretty smashed together `TwoPointXoMutate` implementation of the `ChildMaker` (living in `lib.rs` of all places!) trait that I've been using in all my tests and timing studies, and we started by moving that over to a new `child_maker::two_point_xo_mutate` module which improved the overall structure, and tightened up `lib.rs` a little.

I also made a copy of `TwoPointXoMutate` called `Pipeline` that is currently just a stub, but will hopefully be an example of the composite pattern where we can have sequences of recombinators that will be used to create new genomes from existing genomes. In looking at `Pipeline`, I realized that `TwoPointXoMutate` used the explicit type name in two places where we could have used `Self`. Changing that was straightforward, but it required using a lifetime bound that existed before, but which wasn't being used. I can see why we _do_ need it now, but am not 100% sure why it wasn't necessary when we had `TwoPointXoMutate` instead of `Self`. Presumably the Rust compiler was able able to infer the lifetime when it had the explicit type name, but can't when "all" it has is `Self`, but I'm not sure why that would be.

Before finishing the new `Pipeline` type, I ended up creating a new `Recombinator` trait that has some really interesting typing going on in it:

```Rust
pub trait Recombinator(P, S)
where
 P: Population,
 P::Individual: Individual,
 S: Selector(P),
{
 fn recombinate(
  &self,
  genome: &(P::Individual as Individual)::Genome,
  population: &P,
  selector: &S,
  rng: &mut ThreadRng,
 ) -) (P::Individual as Individual)::Genome;
}
```

I need to get the `P::Individual: Individual` syntax more deeply into my brain; it's come up several times in this project, but I never get it right without someone like #esitsu helping me out. We need that here because `P::Individual` (which is an associated type for `P`) needs to implement the `Individual` trait, so we can get the `Genome` associated type out of it further down.

The `Genome` type really messed with my head, and is definitely something I need to do homework on/practice with. The `(P::Individual as Individual)` "phrase" is saying that we want to treat `P::Individual` as something that implements the `Individual` trait, which is possible because of the `P::Individual: Individual` declaration up above. Once we "know" that it implements `Individual`, then we can add `::Genome` to get the `Genome` associated type from that `Individual`.
That type is then used to declare the type of the `genome` parameter, and the return type of the `recombinate` method.

The general idea of the `recombinate` method is that it takes `genome`, which is some genome it's acting on/using as a starting point, and returns a new genome. It can select additional individuals ("parents") from the `population` using the `selector`, and use the `rng` wherever that's useful.

We then created an empty struct `UniformXO` and had it implement our new `Recombinator` trait. After a lot of thought, I convinced myself that this only made sense on linear structures with the same length, so I made it particular to `Vec(T)`. I think we can do better than that, though, instead using some trait(s) that give us the properties we need (essentially `.len()` and `[i]` indexed access). What we have (`Vec(T)`) is more general than the previous operators in `bitstring.rs`, which are all just `Vec(T)`.

(In retrospect, I think what we really want are things that implement the slice trait.)

================

In the first few months of 2023 (at least to mid-March) I'm streaming at https://twitch.tv/NicMcPhee four times a week:

* Tuesday: 10am-noon CST
* Wednesday: 7-9pm CST
* Saturday: 2-4pm CST
* Sunday: 10am-noon CST

The Tuesday and Sunday slots will be focusing on building the EC system in Rust and on the ice-repos webapp. The Wednesday and Saturday slots will be going through the Advent of Code 2022 puzzles/exercises.

Thanks for watching!