# [Year 2015 Day 12](https://adventofcode.com/2015/day/12) This day provides an introduction to our new [`Parser.JSON`](../../Parser/JSON.md) module, as well as the use of `DList`s[^1] to collect values from an indexed type family into a single collection. ```idris hide module Years.Y2015.Day12 import Control.Eff import Runner ``` ```idris import Structures.Dependent.DList import Parser import Parser.JSON ``` ## Parsing Parse a list of JSON values into a `DList`. `JSONValue`'s type constructor has the signature `JSONType -> Type`, forming what is known as an "Indexed Type Family". Each type of JSON value has a separate type, e.g. a Bool has type `JSONValue TBool`, a String has type `JSONValue TString`, etc. While these are all separate types, they all share the `JSONValue` component of the type constructor, making them members of the same family. Despite being members of the same type family, they still have different types, so we can't just shove `JSONValue`s of different types into, say, a `List JSONValue`, we need a collection with some amount of heterogeneity. While a `List (type : JSONType ** JSONValue type)` would _work_, that introduces various ergonomic headaches, so instead we return a `DList`[^1], a `List` like data structure specifically designed for collecting values from an indexed typed family into a single collection. The parsing logic is otherwise quite simple, we use the `many` combinator to turn the `value` parser, which parses a single JSON value, into one that parses a list of JSON values, and then use `DList`'s `fromList` method to collect the results into a `DList`. ```idris parseJsons : Has (Except String) fs => Has IO fs => (input : String) -> Eff fs (types : List JSONType ** DList JSONType JSONValue types) parseJsons input = do result <- runFirstIO (many value) input case result of Left err => throw $ show err Right result => pure $ fromList result ``` ## Solving A reducer for `DList.dFoldL` that sums all the numbers within the contained JSON Value. The outer function is meant to be called on a top level object, using `DList.dFoldL` on a `DList` of `JSONValue`s, where as the inner function directly reduces a `JSONValue` using `JSON.dFoldL`. ```idris sumNumbers : Double -> (type : JSONType) -> (value : JSONValue type) -> Double sumNumbers dbl type value = dFoldL sumNumbers' dbl value where sumNumbers' : Double -> (type : JSONType) -> (value : JSONValue type) -> Double sumNumbers' dbl TNumber (VNumber d) = dbl + d sumNumbers' dbl _ value = dbl ``` Filter out objects containing a "red" key ```idris noRed : (type : JSONType) -> (value : JSONValue type) -> Bool noRed TObject value = let (types ** vals) = getValues value in case dFind (\t, v => case t of TString => v == (VString "red") _ => False ) vals of Nothing => True Just _ => False noRed _ value = True sumNumbersNoRed : Double -> (type : JSONType) -> (value : JSONValue type) -> Double sumNumbersNoRed dbl type value = case dFilter noRed value of Nothing => dbl Just value => sumNumbers dbl type value ``` ## Part Functions ### Part 1 Parse our JSONs, then fold our `sumNumbers` reducer over them. ```idris part1 : Eff (PartEff String) (Double, (types : List JSONType ** DList JSONType JSONValue types)) part1 = do input <- askAt "input" (types ** values) <- parseJsons input let result = dFoldL sumNumbers 0.0 values pure (result, (types ** values)) ``` ### Part 2 ```idris part2 : (types : List JSONType ** DList JSONType JSONValue types) -> Eff (PartEff String) Double part2 (types ** values) = do let result = dFoldL sumNumbersNoRed 0.0 values pure result ``` ```idris hide public export day12 : Day day12 = Both 12 part1 part2 ``` ## References [^1]: