0 | # Unified interface for any day from any year
  1 |
  2 | This module provides some basic data types for building an application containing all my solutions to all of the parts of all the days across all of the years.
  3 |
  4 | This provides a defensively built API for constructing the overall `Advent` data structure that prevents declaring a day twice or declaring days out of order. 
  5 |
  6 | ```idris
  7 | module Runner
  8 |
  9 | import Control.Eff
 10 | import Structures.Dependent.FreshList
 11 |
 12 | %default total
 13 | ```
 14 |
 15 | # Effectful Parts
 16 |
 17 | The solution to each part of a day is run as an effectful computation, and as the available effects are meant to be the same across both parts, only varying in the type of the error value in the `Except` effect, I construct a type level function to have a single source of truth for this. The `err` type can be any type with a `Show` implementation, but that constraint will be tacked on in the next step.
 18 |
 19 | A `Writer` effect is provided for logging, and a `Reader` effect is provided to pass in the input, just to make the top level API a little bit cleaner. `IO` is also provided, even though the part solutions themselves shouldn't really be doing any IO, this may come in handy if a part needs `IO` for performance reasons.
 20 |
 21 | ```idris
 22 | ||| The effect type is the same in boths parts one and two, modulo potentially
 23 | ||| different error types, so we calucate it here
 24 | public export
 25 | PartEff : (err : Type) -> List (Type -> Type)
 26 | PartEff err =
 27 |   [IO, Except err, WriterL "log" String, ReaderL "input" String]
 28 | ```
 29 |
 30 | # The `Day` Record
 31 |
 32 | The `Day` type groups together an effectful `part1` computation, an optional effectful `part2` computation, the day number, and does some type wrangling to get the type system out of our way on this one.
 33 |
 34 | `part1` and `part2` are allowed independent output and error types, and this record captures `Show` implementations for those output and error types so that we can display them in `Main` where the `Day` is consumed without having to actually know what the types are.
 35 |
 36 | It is often useful to pass a bit of context, such as the data structures resulting from parsing, between `part1` and `part2`, and this is achieved by the erased `ctx` type, which is totally opaque here. The runner code in `Main` will provide the value of the `ctx` type produced as part of the output of `part1` as the input of `part2`.
 37 |
 38 | ```idris
 39 | ||| Model solving a single day
 40 | public export
 41 | record Day where
 42 |   constructor MkDay
 43 |   day : Nat
 44 |   {0 out1, out2, err1, err2 : Type}
 45 |   {auto showOut1 : Show out1}
 46 |   {auto showOut2 : Show out2}
 47 |   {auto showErr1 : Show err1}
 48 |   {auto showErr2 : Show err2}
 49 |   part1 : Eff (PartEff err1) (out1, ctx)
 50 |   part2 : Maybe (ctx -> Eff (PartEff err2) out2)
 51 | %name Day day, day2, day3
 52 | ```
 53 |
 54 | ## Smarter Constructors
 55 |
 56 | The default `MkDay` constructor is slightly cumbersome to use, always requiring _something_ for the `part2` slot, even if there isn't a part 2 yet, and requiring that `part2` be wrapped in a `Just` when there is one, so we provide a pair of constructors for the case where there is only a `part1` and for where there is a `part1` and a `part2` that handle that for us.
 57 |
 58 | ```idris
 59 | namespace Day
 60 | ```
 61 |
 62 | ### First
 63 |
 64 | The `First` constructor only accepts a `part1`, it does the work of filling in `part2` with `Nothing` and setting all of `part2`'s type arguments to `()` for us.'
 65 |
 66 | ```idris
 67 |   ||| Constructor for a day with only part one ready to run
 68 |   public export
 69 |   First : Show err => Show out =>
 70 |     (day : Nat) -> (part1 : Eff (PartEff err) (out, ctx'))
 71 |     -> Day
 72 |   First day part1 =
 73 |     MkDay day part1 Nothing {out2 = ()} {err2 = ()}
 74 | ```
 75 |
 76 | ### Both
 77 |
 78 | The `Both` constructor does a little bit less heavy lifting, the only thing it needs to do for us is wrap `part2` in a `Just`.
 79 |
 80 | ```idris
 81 |   ||| Constructor for a day with both parts ready to run
 82 |   public export
 83 |   Both : Show o1 => Show o2 => Show e1 => Show e2 =>
 84 |     (day : Nat) -> (part1 : Eff (PartEff e1) (o1, ctx')) ->
 85 |     (part2 :ctx' -> Eff (PartEff e2) o2)
 86 |     -> Day
 87 |   Both day part1 part2 =
 88 |     MkDay day part1 (Just part2)
 89 | ```
 90 |
 91 | ## Freshness
 92 |
 93 | We will be using a _Fresh List_ from the [structures](https://git.sr.ht/~thatonelutenist/Structures) package to build defensiveness into the API. A Fresh List structurally only allows you to prepend an element onto it when it satisfies some _freshness_ criteria relative to the elements already in the list.
 94 |
 95 | Here, we compare the day numbers of the two `Day`s using the less-than relationship. Since we are operating on the start of the list when this comparison takes place, this enforces, through type checking, that the resulting Fresh List is sorted in ascending order and that no two `Day`s have the same day number.
 96 |
 97 | ```idris
 98 | ||| Freshness criteria for days
 99 | |||
100 | ||| A day is fresh compared to another if the day number of the former day is
101 | ||| strictly less than the day number of the latter day
102 | |||
103 | ||| This ensures that the days list is always in incrimenting sorted order
104 | ||| (since we are consing to the front of the list) with no duplicate days
105 | public export
106 | FreshDay : Day -> Day -> Bool
107 | FreshDay x y = x.day < y.day
108 | ```
109 |
110 | # The `Year` Record
111 |
112 | The `Year` record collects a number of `Day`s into a single Fresh List for the year, and is mostly just a simple container.
113 |
114 | ```idris
115 | ||| Collect all the days in a given year
116 | public export
117 | record Year where
118 |   constructor MkYear
119 |   year : Nat
120 |   days : FreshList FreshDay Day
121 | %name Year year, year2, year3
122 | ```
123 |
124 | ## Freshness
125 |
126 | Much like `Day`s are stored in a `FreshList` in `Year`, `Year`s will be stored in a `FreshList` in `Advent`, so we need to provide a freshness criteria for `Year` as well. We do so by applying the less-than relationship against the year number of the two `Years`, for the same reasons and with the same results as with `FreshDay`.
127 |
128 | ```idris
129 | ||| Freshness criteria for years
130 | |||
131 | ||| A year is fresh compared to another if the year number of the former year is
132 | ||| strictly less than the year number of the latter year
133 | |||
134 | ||| This ensures that the years list is always in incrimenting sorted order
135 | ||| (since we are consing to the front of the list) with no duplicate years
136 | public export
137 | FreshYear : Year -> Year -> Bool
138 | FreshYear x y = x.year < y.year
139 | ```
140 |
141 | # The `Advent` Record
142 |
143 | The `Advent` record collects a number of `Year`s in much the same way that `Year` collects a number of days, sorting the `Year`s in a `FreshList` to provide API defensiveness.
144 |
145 | ```idris
146 | ||| Collect all years
147 | public export
148 | record Advent where
149 |   constructor MkAdvent
150 |   years : FreshList FreshYear Year
151 | %name Advent advent, advent2, advent3
152 | ```
153 |
154 | ## Methods
155 |
156 | ```idris
157 | namespace Advent
158 | ```
159 |
160 | ### locate
161 |
162 | This is a utility function that searches the `FreshList` of `Year`s for the provided year number, then searches the resulting `Year`, if one exists, for the provided day number.
163 |
164 | We don't have to worry about potentially encountering duplicates here because of the freshness restriction. 
165 |
166 | ```idris
167 |   ||| Attempt to locate `Day` entry corresponding to the provided day and year numbers
168 |   export
169 |   locate : (year, day : Nat) -> Advent -> Maybe Day
170 |   locate year day advent = do
171 |     year <- find (\x => x.year == year) advent.years
172 |     find (\x => x.day == day) year.days
173 | ```
174 |