|
@@ -1,7 +1,55 @@
|
|
|
//
|
|
|
-// Remeber excerices 55-57 with tagged unions.
|
|
|
+// Remember our ant and bee simulator constructed with unions
|
|
|
+// back in exercises 55 and 56? There, we demonstrated that
|
|
|
+// unions allow us to treat different data types in a uniform
|
|
|
+// manner.
|
|
|
//
|
|
|
-// (story/explanation from Dave)
|
|
|
+// One neat feature was using tagged unions to create a single
|
|
|
+// function to print a status for ants *or* bees by switching:
|
|
|
+//
|
|
|
+// switch (insect) {
|
|
|
+// .still_alive => ... // (print ant stuff)
|
|
|
+// .flowers_visited => ... // (print bee stuff)
|
|
|
+// }
|
|
|
+//
|
|
|
+// Well, that simulation was running just fine until a new insect
|
|
|
+// arrived in the virtual garden, a grasshopper!
|
|
|
+//
|
|
|
+// Doctor Zoraptera started to add grasshopper code to the
|
|
|
+// program, but then she backed away from her keyboard with an
|
|
|
+// angry hissing sound. She had realized that having code for
|
|
|
+// each insect in one place and code to print each insect in
|
|
|
+// another place was going to become unpleasant to maintain when
|
|
|
+// the simulation expanded to hundreds of different insects.
|
|
|
+//
|
|
|
+// Thankfully, Zig has another comptime feature we can use
|
|
|
+// to get out of this dilema called the 'inline else'.
|
|
|
+//
|
|
|
+// We can replace this redundant code:
|
|
|
+//
|
|
|
+// switch (thing) {
|
|
|
+// .a => |a| special(a),
|
|
|
+// .b => |b| normal(b),
|
|
|
+// .c => |c| normal(c),
|
|
|
+// .d => |d| normal(d),
|
|
|
+// .e => |e| normal(e),
|
|
|
+// ...
|
|
|
+// }
|
|
|
+//
|
|
|
+// With:
|
|
|
+//
|
|
|
+// switch (thing) {
|
|
|
+// .a => |a| special(a),
|
|
|
+// inline else |t| => normal(t),
|
|
|
+// }
|
|
|
+//
|
|
|
+// We can have special handling of some cases and then Zig
|
|
|
+// handles the rest of the matches for us.
|
|
|
+//
|
|
|
+// With this feature, you decide to make an Insect union with a
|
|
|
+// single uniform 'print()' function. All of the insects can
|
|
|
+// then be responsible for printing themselves. And Doctor
|
|
|
+// Zoraptera can calm down and stop gnawing on the furniture.
|
|
|
//
|
|
|
const std = @import("std");
|
|
|
|
|
@@ -9,7 +57,7 @@ const Ant = struct {
|
|
|
still_alive: bool,
|
|
|
|
|
|
pub fn print(self: Ant) void {
|
|
|
- std.debug.print("Ant is {s}.\n", .{if (self.still_alive) "alive" else "death"});
|
|
|
+ std.debug.print("Ant is {s}.\n", .{if (self.still_alive) "alive" else "dead"});
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -21,11 +69,13 @@ const Bee = struct {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+// Here's the new grasshopper. Notice how we've also added print
|
|
|
+// methods to each insect.
|
|
|
const Grasshopper = struct {
|
|
|
distance_hopped: u16,
|
|
|
|
|
|
pub fn print(self: Grasshopper) void {
|
|
|
- std.debug.print("Grasshopper hopped {} m.\n", .{self.distance_hopped});
|
|
|
+ std.debug.print("Grasshopper hopped {} meters.\n", .{self.distance_hopped});
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -34,6 +84,10 @@ const Insect = union(enum) {
|
|
|
bee: Bee,
|
|
|
grasshopper: Grasshopper,
|
|
|
|
|
|
+ // Thanks to 'inline else', we can think of this print() as
|
|
|
+ // being an interface method. Any member of this union with
|
|
|
+ // with a print() method can be treated uniformly by outside
|
|
|
+ // code without needing to know any other details. Cool!
|
|
|
pub fn print(self: Insect) void {
|
|
|
switch (self) {
|
|
|
inline else => |case| return case.print(),
|
|
@@ -42,27 +96,32 @@ const Insect = union(enum) {
|
|
|
};
|
|
|
|
|
|
pub fn main() !void {
|
|
|
- var my_insects = [_]Insect{ Insect{
|
|
|
- .ant = Ant{ .still_alive = true },
|
|
|
- }, Insect{
|
|
|
- .bee = Bee{ .flowers_visited = 17 },
|
|
|
- }, Insect{
|
|
|
- .grasshopper = Grasshopper{ .distance_hopped = 32 },
|
|
|
- } };
|
|
|
-
|
|
|
- // The daily situation report, what's going on in the garden
|
|
|
- try dailyReport("what is here the right parameter?");
|
|
|
-}
|
|
|
+ var my_insects = [_]Insect{
|
|
|
+ Insect{ .ant = Ant{ .still_alive = true } },
|
|
|
+ Insect{ .bee = Bee{ .flowers_visited = 17 } },
|
|
|
+ Insect{ .grasshopper = Grasshopper{ .distance_hopped = 32 }, },
|
|
|
+ };
|
|
|
|
|
|
-// Through the interface we can keep a list of various objects
|
|
|
-// (in this case the insects of our garden) and even pass them
|
|
|
-// to a function without having to know the specific properties
|
|
|
-// of each or the object itself. This is really cool!
|
|
|
-fn dailyReport(insectReport: []Insect) !void {
|
|
|
- std.debug.print("Daily insect report:\n", .{});
|
|
|
- for (insectReport) |insect| {
|
|
|
- insect.print();
|
|
|
+ std.debug.print("Daily Insect Report:\n", .{});
|
|
|
+ for (my_insects) |insect| {
|
|
|
+ // Almost done! We want to print() each insect with a
|
|
|
+ // single method call here.
|
|
|
+ ???
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// Interfaces... (explanation from Dave)
|
|
|
+// Our print() method in the Insect union above demonstrates
|
|
|
+// something very similar to the object-oriented concept of an
|
|
|
+// abstract data type. That is, the Insect type doesn't contain
|
|
|
+// the underlying data, and the print() function doesn't
|
|
|
+// actually do the printing.
|
|
|
+//
|
|
|
+// The point of an interface is to support generic programming:
|
|
|
+// the ability to treat different things as if they were the
|
|
|
+// same to cut down on clutter and conceptual complexity.
|
|
|
+//
|
|
|
+// The Daily Insect Report doesn't need to worry about *which*
|
|
|
+// insects are in the report - they all print the same way via
|
|
|
+// the interface!
|
|
|
+//
|
|
|
+// Doctor Zoraptera loves it.
|