Re-splitting drasil-lang
(A lot of the analysis behind this issue is in #2883; specifically in the 'shallow analysis' for drasil-lang).
The analysis there accurately describes how drasil-lang defines a whole slew of encodings. Some of these are inter-dependent, some are not. The type dependency graph and the module dependency graph both illustrate this.
It would make a lot of sense to split this up into much smaller pieces. This can be done incrementally. The ones that are 'at the top' of the graph (in dependency order) can likely be hived off most easily. Also, I think there are things at the very bottom that can be split too.
To be precise, I think the following should be relatively easy to split:
- components of a notebook/document
- natural language
- people
- references & citations
- URIs
There will remain dependencies between
- mathematical languages
- constructs
- derivations
The usefulness of doing this split is definitely going to be that some of the dependencies on drasil-lang will shift to being dependencies on much smaller bits.
Reminder to self: NamedArgument should likely be moved to drasil-code since it's "code-focused" and seemingly tightly bound to internal logic in drasil-code.
Related to #3307: We have a few code-related chunks in drasil-lang that I started moving to drasil-code, where I think they should belong. However, I'm blocked by a circular dependency when I try. I guess this is related to us previously having and then removing drasil-code-base.
Path to finding the issue:
-
drasil-lang's definition of a "document" language (another issue perhaps) that depends onCodeExprfor code blocks. I don't think it should depend onCodeExprbecauseCodeExpris only for expressions, not whole code blocks. Temporarily changing the dependency toExprshould be fine, but a more appropriate input for code blocks is probably arbitrary text with a language tag or GOOL if we want to be restrictive to OO languages. - Other than the above issue,
drasil-langdoes not depend onCodeExprotherwise, so we can compiledrasil-langjust fine. - However, when we move
CodeExprintodrasil-code, we run into a circular dependency withdrasil-printersbecausedrasil-printerscarries a printer that convertsCodeExprinto its document language.
Now the question is: should the printing code go inside drasil-code or drasil-printers (or something else)?
So, I searched for how drasil-code relies on drasil-printers:
> rg "import Language.Drasil.Printers" -ths drasil-code
drasil-code/lib/Language/Drasil/Chunk/Code.hs
15:import Language.Drasil.Printers (symbolDoc)
drasil-code/lib/Language/Drasil/Code/Imperative/Modules.hs
44:import Language.Drasil.Printers (SingleLine(OneLine), codeExprDoc)
drasil-code/lib/Language/Drasil/Code/Imperative/Generator.hs
33:import Language.Drasil.Printers (SingleLine(OneLine), sentenceDoc)
drasil-code/lib/Language/Drasil/Code/Imperative/Descriptions.hs
20:import Language.Drasil.Printers (SingleLine(OneLine), sentenceDoc)
drasil-code/lib/Language/Drasil/Code/Imperative/Comments.hs
10:import Language.Drasil.Printers (SingleLine(OneLine), sentenceDoc, unitDoc)
drasil-code/lib/Language/Drasil/Code/Imperative/WriteInput.hs
11:import Language.Drasil.Printers (SingleLine(OneLine), exprDoc, sentenceDoc,
drasil-code/lib/Language/Drasil/Code/Imperative/WriteReadMe.hs
6:import Language.Drasil.Printers (makeMd, introInfo, verInfo, unsupOS,
[added in post:] extLibSec, instDoc, endNote, whatInfo)
I think it's fair for drasil-code to rely on drasil-printers because drasil-code houses code-generation code.
Looking at drasil-printers, it contains...
-
Printing: an AST (Document) for documents we would commonly encounter, with features for tables, sections, equation blocks, code blocks, lists, figures, etc., -
Printing.Import: various printers for things indrasil-langto the AST mentioned above, -
Markdown: a Markdown encoding of sorts largely focused on generating Markdown files related to the generated artifacts, -
JSON: a specific kind ofJSONencoding, focused on JupyterLab notebooks (which are encoded as JSON files), -
Debug: a chunk-dump tool which should be removed altogether ultimately with #2873 (one day), -
DOT: a specific-ish kind of GraphViz/.dotencoding largely focused on generating the.dotdiagrams we're interested in, -
HTML: an HTML renderer forDocumentwith some goodies for things we're interested in, -
Plain: aDocumentrenderer for plaintext variants of the SRS documents, and -
TeX: aDocumentrenderer focused on building LaTeX documents, with some goodies for math and bibtex.
Some 'aside' observations:
-
Documentis like our own pandoc of sorts! That's neat! - The
Markdownmight be more appropriately placed elsewhere. - We might want to build actual JSON, Markdown, GraphViz, and HTML encodings too.
- We don't use
Statein any of the printers here, but we do in GOOL.Statewould allow us to do some nifty things, I believe, specifically for ID generation and such for HTML, footnote creation, traceability matrices from texts, etc. Have we ever discussed usingStatein general for Drasil?
The more important observation:
-
drasil-printersanddrasil-codehave different philosophies in regard to artifact generation.drasil-printerscarries things that are closer to our terminal chunks (/artifacts) whiledrasil-codecarriers 'higher level' knowledge. For printing-related matters,drasil-printers'pulls in' the higher-level things fromdrasil-lang, whiledrasil-codeexports lower-level things fromdrasil-printers.
Since we're largely dealing with 'synthetical' relationships, I think I tend towards the latter: 'higher level/problem knowledge' package carrying the information about how they should be rendered into the lower level/terminal chunk artifacts. I started work on that in 269b55f1d48a454022735fd9bb4a2c7c4f035d5e (looks larger than it is), but then I ran into another import issue. Namely, drasil-code would need to rely on some new things from drasil-printers:
import qualified Language.Drasil.Printing.AST as P
import Language.Drasil.Printing.PrintingInformation (PrintingInformation, ckdb, stg)
import Language.Drasil.Printing.Import.Helpers
(lookupC, parens)
import Language.Drasil.Printing.Import.Literal (literal)
import Language.Drasil.Printing.Import.Symbol (symbol)
I think that all of these things make sense -- printing to the Document language (so, the Document language itself), printing information, a chunk db, symbol stage, a chunk lookup function, a 'parenthesizing expressions' function for the document language, a literal renderer, and a symbol renderer. However, it looks like there was a design choice specifically made in the past to not export these.
So, it looks like there is a larger design decision to make, and I'm wondering: how shall we proceed?
Shallow answer to just the part about drasil-lang's document language depending on CodeExpr: maybe we could make that part polymorphic, i.e. the document language would have a type parameter that represents a to-be-filled type (i.e. CodeExpr in practice). That way the document language is explicitly oblivious to the details of what 'code' is / will be.
I think the drasil-code style is also more in-tune with the semantics paper/"describe syntax with a semantic domain in mind" style. Seeing this idea through, an alternative view of drasil-printers might have:
- The creation of a
drasil-artifactspackage, carrying various encodings for external things (HTML, Markdown, Makefile, etc.). Note that GOOL would not be included because it sits atop of the code. - A shift of all the renderers closer to their syntax definitions.
I'm not sure if the contents of drasil-printers is generic enough either (there is a lot of implicit knowledge specific to the generators in Drasil), so it might also be good to split it up and move its pieces closer to the areas its most relevant to.
I don't think these kinds of changes are too huge, it's really just shifting things around to stabilize the dependency chain (or I guess come up with one that might fit the current story better).