Things I'm not supporting in v1:
- Record syntax patterns
- Lots of complications:
- Record update is actually very annoying to represent, as the desugared version is an updater for every field
- Normal forms are more complicated: we either need a new
Sym
just for objects defined through record syntax or a mapping to the datatype declaration (cf. 3.) - Record patterns are more complicated: at the very least, we need to use the existing auto-generated function to access the given record, and at worst, if that one is actually useless, we need to write our own. Hopefully too, this is compatible with our internal representation. If the representation is only the function call syntax (i.e.
HsApp Con ...
), then we need to cross-reference against the datatype declaration.
- Lots of complications:
- Tuple sections
- Need to wrap within a lambda that we construct ourselves in order to desugar (after typechecker, the syntax is still preserved, with
Missing
vs.Present
tuple fields)
- Need to wrap within a lambda that we construct ourselves in order to desugar (after typechecker, the syntax is still preserved, with
- Explicit type applications
- Something something phantom types something something. There might actually not be a problem with this: we just ignore it, unless the type being applied is
IO
.
- Something something phantom types something something. There might actually not be a problem with this: we just ignore it, unless the type being applied is
- Recursive threading (e.g.
replicateM_ 10 (forkIO $ do ...)
) and its more subtle incarnations (e.g.op $ replicateM 10 (share (merge printSub))
)- This functionality is critical for this tool to be usable, but it's also a substantial challenge. It's not easy to identify recursion, and the analysis of an unknown number of forks needs to be distinct from the analysis of exactly one for the given condition being analyzed. In particular, the fact that there could be 0 or >= 2 threads that pop up here is a very important condition. For ReactiveX race conditions the ">= 2" case will change results.
- Annotating decomposed/flowed values with rationale
- This is an important piece of functionality for this tool, but also requires a lot of extra information to haul around. Before tackling this, it would be wise to consult typecheckers (e.g. Hack's) to see how they build error messages from multiple points of inference.
- Recursive applications of partial functions (e.g.
fix (fib_with 2)
)- Also in part because there is functionality to identify recursion statically at the moment. The code that checks the stack for cycles in
reduce_deep
is too sensitive at the moment. If a function sitting in the same spot is called more than once, the recursion will quit on that branch altogether. However, partial functions can be parameterized in different ways along the branch with non-trivial effect.
- Also in part because there is functionality to identify recursion statically at the moment. The code that checks the stack for cycles in
unsafePerformIO
and any other IO unboxing (i.e.IO a -> a
), to enforce that all IO refs be within IO blocks- Confine the magic to IO, so we can confine the reference processing to just the
do
blocks and monadic operations (>>
,>>=
, etc.)
- Confine the magic to IO, so we can confine the reference processing to just the
- Parallel monad comprehensions (see
HsDo
ParStmt
case)- For lack of understanding their exact semantics. How does this interact with the
fork
code?
- For lack of understanding their exact semantics. How does this interact with the
- Type families or
cast
, i.e. type-driven logic- Could vastly reduce our specificity: imagine a result from
everything
that just picks up... everything.
- Could vastly reduce our specificity: imagine a result from
- RecursiveDo
- To great benefit to simplicity, the recursion detection engine assumes that within
do
, variables don't refer to each other outside oflet
recursive/mutual bindings
- To great benefit to simplicity, the recursion detection engine assumes that within