These aspects should be implemented from the start for a programming language. For some of them, we have some insight on how to implement that feature more easily than usual.
Key insight: take program manipulation concepts that are usually external to the language, and integrate them into the language, using first-class environments, first-class continuations, laziness (for macros) and syntax quotations (for macros).
| Feature | Insight |
|---|---|
| evaluator | |
| serialization of all run-time values, and of the entire program state | language design (entire state representable by a term) |
| diff tool (e.g. sexp-diff for sexps), with JS or server-side implementation for to highlight diffs in online versionned repositories | generic row map in metalanguage |
| debugger (with domain-specific extensions http://scg.unibe.ch/research/moldabledebugger) | defunctionalize(cps(evaluator)) + explicit env + continuations to make the stack explicit (+ lazy on program to pause before evaluating a term?) |
| instrumentation for performance / profiler | use debugger's step-by-step or sampling via GC API or instrumentation by injecting code (via macro) |
| code coverage, with on-line web interface to display of the code coverage results + doc-coverage | done via instrumentation |
| communicate with native libraries | in-place serialization / expressive type system |
| multicore (can be simple "parallel_for" / "parallel_map" or explicit thread management) | implementation via native library |
| garbage collection | ~25 lines of code / reference counting |
| lexer, parser, syntax highlighting, projectional editor | Christian for 1 & 2. Have a good GUI library for 3. |
| type inference / propagation | ! unidirectional propagation, domain-specific type systems |
| typechecking | ! interactively explore type errors: better than msgs |
| simplification passes | Nanopass |
| compilation to JS | don't rely on single-architecture native libraries |
| compilation to native executables | no need to optimize at the beginning |
| module system | ! row polymorphism |
| package system | purely functional package management (Nix) |
| package repository with browser (sort & filter by criteria) | web & social stuff, use compilation to JS |
| * e-mail you when a package you maintain is broken on the "dev" branch | |
| * e-mail you when a dependency of one of your packages has a security vulnerability, or has a breaking update. Show status to reward quality. | |
| centralized online documentation repository with clickable identifiers in code fragments and cross-references | good resolution of ids (see refactoring) |
| * plus search engine to find functions by their type, description, module that provides them etc. (hoogle) | use compilation to JS + unification algo |
| * search engine for error messages, translatable error messages | error codes (Error FOO1234: …) + use translation library from the start + interactive error messages (expand info, go to documentation, …) instead of plain strings |
| browse source code with hyperlinks and diff between versions | good resolution of ids (see refactoring) |
| literate programming | explicit env, macro-like facilities |
| macros | laziness + quote syntax + inlining |
| API for IDE | explicit env, refactoring = expansion of a single macro |
| * Go to definition | |
| * rename across files | |
| * rename files (modules) themselves | |
| * other transformations for occurrences of an identifier (eta-expansion etc.) | |
| * other refactoring operations | |
| * display compiler warnings, lint info, type info, static analysis info | annotations produced by macro-like things |
| indenter (indents but does not change newlines), possibly (likely?) customizable | ! find a good framework to express indentation rules |
| Policy for package API evolution & security vulnerability patching | ! Run tests of previous version to see that they all work but may introduce warnings; IDE support for deprecating features and bumping the version number; provide a rewriting: if the rewriting validates a proof that the behaviour did not change it can be applied automatically, else manually or warning because the actual use follows an undesired pattern |
| pretty-printer (indents and inserts newlines to try to fit within a certain width), possibly (likely?) customizable | ! same as above |
| * graphical / annotated pretty-printer | good GUI framework |
| queries on the source code (find functions with more than X lines, show dependency graph, …) | code inspection via macro-like things |
| every IDE, runtime and language feature available in a web interface (trylanguagefoo.io demo for new users and playground) | use compilation to JS + good GUI framework |
| compilation should produce a single-file output | enables purely functional package management & pure build system |
| extraction of the dependencies of a file | first-class environment helps |
| package management: should be easy to export the state to a single file, to make it easy to create packages in a third-party package manager. | single-file output + dependencies extraction |
| static analysis | just make sure there's an API for that from the start |
| linter | just make sure there's an API for that from the start |
| "modes": build, doc, test, runtime (for dependencies, tree-shaking etc.) | ! think about it, relates to packages and builds |
Notation "p => e" := (mk_pattern p e) (p in scope pattern, e in scope expression)
Notation "p | p'" := (combine_patterns p p') (in scope match_cases)
Notation "match x with cs end" := (mk_match x cs) (cs in scope match_cases)
Notation "p => e" := (mk_pattern p e) (p in scope pattern, e in scope expression)
Notation "p | .. | p''" := (combine_patterns p .. (combine_patterns p' p'') ..) (in scope match_cases)
Notation "match x with cs end" := (mk_match x cs) (cs in scope match_cases)
A way to express the notation
match x
case p₁ => e₁
…
case pn => en
end
and the notation:
(case p₁ => e₁
…
case pn => en)
If:
Notation ""if" a "then" b "else" c" (c bounded-by-start-and-end-kws)
Notation ""if" a "then" b" (b bounded-by-start-and-end-kws)
allowed uses:
if true then f 1 2 3 else (g 4 5)
if true then f 1 2 3 else (g 4 5)
allowed if the precedence of function calls and arithmetic comparable in some direction (less than? or should it be greater-than?) with the if:
(if true then f 1 2 3 else g 4 5) (* delimiter at the start of the if indicates that it's ± safe to parse till the closing paren, might be a bad idea *)
forbidden uses:
if true then f 1 2 3 else g 4 5 (* could be followed with other things *)
if true then f 1 2 3 (* could be followed with other things *)
(3 + if true then f 1 2 3 else g 4 5) (* could parse as (3 + (if true then f 1 2 3 else (g 4)) 5) or (3 + (if true then f 1 2 3 else (g 4 5)))
Nested pairs in the left:
Notation "( x , y .. , z )" => (pair .. (pair x y) .. z)
Notation "( x , ... , z )" => (pair .. x .. z)
Two successive keywords:
Notation ""let" "rec" a ":=" b "in" c => (letrec a b c)
Custom notations for strings (escaping, $var substitution, @{code} substitution, …)
Relative precedences
Custom top-level parser: e.g. for literate programming, custom syntax (ala prolog, ala pascal, ala scheme, ala C).
Loading a library can modify the parser. What about let open … in …?
Unparsing (pretty-printing)
Is it possible to rename the lexer tokens used by a notation?
E.g. if a module provides the notation match x with cs end, it could be imported while renaming match to switch, with to in and end to done.
If this throws an error at import-time because it conflicts with existing uses of in that's fine.
Preferably, there would be a conservative way to know for sure that the notations Foo and Bar from modules A and B won't interact with each other, as long as some (possibly stringent) conditions are met, e.g.
The goal is to know, when modifying a notation in a library, if this is going to be a significant breaking change (another alternative would be to have IDE support to do the alpha-renaming in a non-ambiguous way).
match goal with … end is the way to grab a random hypothesis, but:matchQed. to simplify the proof term, e.g. discarding administrative shuffling due to intros & reverts.info)match … end term is awkward
(cannot put a pattern in place of the constructor name (can be done via case_eq hackery), cannot put a pattern in place of the in ?t clause)Search from the tactic language (possibly restricted to a given module, it can than be done with row polymorphism & row introspection)forall (a : nat), bool will often show as forall (_ : nat), bool or nat -> boolintros ??? H. where the ? do not correspond to already-named variables (i.e. to prevent accidental automatic name generation).(forall (implicit a), list a) can be passed where a list int is expected).
In coq, Definition x := fun {t} => @nil t. Compute cons 1 x works, but Compute cons 1 (fun {t} => @nil t). does not. Also, the implicitness is lost in Definition x := fun {t} => @nil t.fix n . (Nat, (O | S n)) where the type tag Nat can be used to match on the type.if b then nat else nat is epxected, but if you put a tactic instead of the nat value you can prove that the type expression always reduces to nat and then pass one as an argument (it generates some boilerplate in the final term, but that's okay).:= and then used, e.g. forall (t := nat) (x : t), t instead of forall x : nat, nat. This means that there would be only two constructions: forall var := global_constant_or_arbitrary_expression and forall var : locally_bound_type_variable. Not sure if that actually means less constructions or if it doesn't matter?at level X where X is a random number is just user-hostile)( a , b , .. , c ) are brittle, there are a bunch of formulations that don't work and it's hard to understand why.some_expression. For now, one has to do let X := MARKER (some_expression) in ltac:(some_tactic) : _ and let some_tactic grab the MARKER (some_expression) from the environment, transform it, detect the type of the result via let t := type of transformed_expression in …, unify the goal with that type and solve the goal using the transformed expression. That's jumping through a lot of hoops. Probably can be solved with a good notation + tactic that do this once and for all.Queries on the code
Search (forall b a, S a < S b -> a < b). returns no result, but Search (forall a b, S a < S b -> a < b). finds a lemma.
MLF's unordered quantifiers and unification that is allowed to lift quantifiers should solve this.Print that takes an arbitrary expression (to show what has been understood after some ltac:() and _ (possibly in parse-only notations) have been solved). The closest is to Declare Reduction print := unfold dummy. and then do Eval print in arbitrary_expression..Declare Reduction print := unfold dummy. and then Eval print in some_transformation (arbitrary_expression)., where some_transformation jumps through hoops with a Notation to grab the arbitrary_expression and feed it ton an ltac:(…).IDE
Replay proof some_thm. …. End replay. command, currently one has to write Goal replay proof some_thm. init_replay_proof. …. Qed.).TODO.
TODO.
TODO.
TODO.