Follow the instructions in src/Lib.hs by completing all the tasks marked in the comments (e.g. -- 1) ...).
Answer the following reflection questions and add them to your META.md
From hw01-1, hw02-1, and this assignment, what coding topic or implementation do you feel you could most easily teach to another cs student at your level? Which do you feel would be most difficult to?
Finish the usual material for your META.md
Step-by-step instruction
Looking ahead, you will be designing a domain specific language for some
programming domain of your choice. First, you will put on the hat of
a programmer in that domain. You will write programs in the domain using
bare haskell abstractions and design patterns.
Also, the tools we will be
using to write the metaprograms themselves are used using bare haskell abstractions
and design patterns. Specifically, monads are the design pattern that we saw
in the Template Haskell paper (the Q Monad) to sequence compilation, and monads will be the design pattern we use
to implement our parsers (the Parsec Monad) to sequence parsing a string.
There are many ways to teach these design patterns,
and some are more helpful than others for particular people.
Below is a bottom up approach that Matthew used to learn them that starts with
something you are familiar with (map functions) and works through increasing
levels of abstractions until we arrive at monads.
This method of learning monads might not work for everyone, and so the next
homeworks will provide a top-down approach to learning monads. You will
learn by doing in the template haskell domain and the parsec domain.
So, if at any point you feel that this homework isn't helping you learn or
apply the design patterns. Stop! You will be able to achieve the same learning
objectives on the next assignments
The following video series aims to provide some muscle memory building up to Monads.
For a data structure (some type :: * -> * ) to be a Monad, it must also
be an Applicative. And in turn, all Applicatives must be Functors. So,
starting with functionality you are used to, map, we will be playing
with the instances of these design patterns for common data structures.
The main idea from this homework is when defining a data structure,
designers implement the typeclasses for Functors, Applicatives, and Monads
to prescribe how to connect computations over the data structure together.
Functors - mapping over datastructures
Many libraries for domain specific programming in Haskell will simply export an
api as a collection of functions. One of the first ways to use these apis is to
weave them through the data structures in your programs. So, in this video, you
will gain some practice with Functors, the design pattern for describing how
to map an arbitrary function over values in a data structure.
Applicatives - applying a wrapped function over datastructures
Often, we will partially apply a library function over our data structures.
When we partially apply a function, we get a function back. When we map it over
our data structure we must then get a function wrapped in that data structure.
So, we need the data datastucture to tell us what that means.
Specifically, applying a function
wrapped in a Maybe (e.g. Maybe (Int -> String)) might be very different
than a list of functions (e.g. [Int -> String]). So, because we often
encounter the pattern of applying wrapped functions, we use the
applicative typeclass to describe the data structures that can apply functions
held within.
In this video, you will be playing some instances of the Applicative typeclass.
Monads - binding together data constructors
You are familiar with algebraic data types whose data constructors are public.
Just, Left, Right, Cons, Tuple syntax, etc. are all data constructors and patterns
that construct and deconstruct data structures. When we want to compute over
multiple uses of a constructor, we often use case statements or functions to
apply the constructor, unpack the data, do the computation, and repack the result.
For many data constructors, the act of unpacking and repacking the data is the same,
and it doesn't matter what the data is. This design pattern is represented as the
Monad typeclass. In this video, you will be implementing two data constructors
and chaining them together either using >>= or do-notation.
Often, designers hide the internals of their data structures
so that users cannot get into a bad state.
But, in order to still be usable, they need to expose a way for computations to
be chained together. Two of the data structures that hide their internals you will commonly encounter are State and IO.
Both of these data constructors instantiate the Monad typeclass to describe how
side effecting actions are sequenced. Users compose bigger side-effecting
computations out of the primitives by the Monad methods.
In this video, you will use the primitives for these data structures, and
the monad methods (either >>= or do-notation)
practice sequenced, side-effecting computation.
I'm here but I still have questions (even if I don't know what they are)!
It's completely understandable and expected to still have questions at this point.
Steps you can take to help unstick yourself: