Jay's Projects
These are some projects I'm currently “working” on. More realistically, these are some vague ideas that I have, that I’ve made some amount of progress on. Let me know if you’re interested in any of them!
Right now, this is actually just one big project. I’ll add more stuff here and organize it better when I have time.
Haskell library for Arrowized Functional Reactive Programming in FIRST Robotics
Since I was in high school, I’ve been involved in FIRST robotics
competition. I’ve generally used Java and WPILib, but been frustrated by
the poor composability and confusing semantics of Command
s
and Subsystem
s. Internally, I’ve always thought about robot
programs as several small, stateful pieces with inputs and outputs wired
together, even if that was far from how I implemented them. A few years
ago, I discovered an approach called Arrowized Functional Reactive
Programming, which is everything I’ve wished for and more. I’ve started
work on a Haskell library to be able to use AFRP in FIRST robotics.
Idea
Arrowized Functional Reactive Programming is a style of programming for building interactive systems in a purely functional language. The particular formulation I’m thinking of is described in the paper Functional Reactive Programming, Refactored, by Perez, Bärenz, and Nilsson.
One of the motivating examples of AFRP is robotics. You can think of the whole robot program as consuming a stream of inputs from sensors and controllers, and producing a stream of outputs to actuators, with the interesting, purely functional logic in the middle. We call this core bit of logic a signal function.
The core idea of AFRP is that you can build simple, re-usable signal functions like “PID controller” or “low-pass filter”, and then build up more complex programs by composing them with various combinators.
Haskell is especially well-suited to this for a few reasons. First,
it is purely functional, so this separation between IO and pure logic can
be enforced by the type system. This makes it possible to apply
equational reasoning, which in my opinion is a huge conceptual win.
Second, most of the combinators actually just come
from Arrow
and Monad
classes. Haskell already
has a huge ecosystem around these classes, including do
and proc do
notation. The upshot of this is that all the
normal “control flow” operations work on signal functions, and we can
just “wire inputs to outputs”.
This is just a summary. For the full story, you should really check out the links above.
What needs to be done
Core implementation of AFRP in Haskell
This is done, but not by me, in the wonderful dunai package on Hackage.
A library of signal functions useful for FRC
I’ve started work on this. The thing I’m most proud of so far is a
composable “teleop controller”, which allows you to swap out parts of a
signal function in response to triggers, like button presses. The
controllers can be nested to an arbitrary depth, so you can have
“sub-sub-systems”. The semantics can be defined by a few equations, most
of which are just the Arrow
and Semigroup
laws
(here the semigroup operation is just providing alternative behaviors for
different “modes”). In WPILib, this would have been handled by canceling
one command when another command has conflicting requirements. My
approach has the advantage that the type system guarantees that each
subsystem has exactly one thing sending it outputs at any given time, and
is in my opinion easier to reason about.
There are several other ideas I’d like to try out that can come later,
such as ways to automatically manage preferences or automatically send a
signal function’s inputs and outputs to the dashboard for
debugging. Generic
could be useful for serializing and
deserializing NetworkTables data. (Note: we cannot use Template Haskell
when cross compiling, so we need other methods of managing
boilerplate!)
Abstractions for describing hardware
While the pure logic is handled by signal functions, at some point
we’ll need to specify how to do IO. While we could just manually chain
together IO actions for opening, reading, writing, and closing hardware,
I’d like to do something more declarative. I’ve found that hardware can
be modeled as a Profunctor
. This means you can compose pure
functions on both the input and the output. It’s also
an Applicative
, which means you can combine hardware of
different types. This approach makes it easy to tag hardware with
metadata, such as port mappings, for static analysis. For notation, I’ve
found that the language extensions ApplicativeDo
and RecordWildCards
make for very concise and readable
code.
Bindings to the Hardware Abstraction Layer (and vendor libs)
This is partially done. I have enough of the bindings done that I can run a Haskell program against HALSim, and use PWM and other simple hardware. I don’t see any major technical obstacles here.
I plan to implement bindings as two packages: an API package and an implementation package. Your main robot program would be a library that depends on the API, and then you’d also have a “runner” program that depends on the implementation. This would allow you to run tests (including port map validation) without having to worry about linking to native libraries.
Building a cross-compiler targeting the RoboRIO
In theory, I’ve done this. After a lot of fiddling, I was able to
build a cross-compiler that used the FRC toolchain, and linked against
the appropriate versions of libraries. From poking around
with readelf
it seems to be doing the right thing. I’ve yet
to test it on real hardware.
Real-world examples
I started this, using the 2018 game as an example, since my team’s robot from that year had the simplest hardware. This would be a good opportunity to demonstrate all the possibilities for offline testing this opens up.
Documenting and teaching
This is the most important part, and also the hardest part. Clearly, it will need to be more accessible than this jumble of thoughts. I’ve written up some notes, but nothing substantial yet.