Full Code of Gabriel439/grace for AI

main 9230b0a0fc60 cached
847 files
897.1 KB
235.5k tokens
1 requests
Download .txt
Showing preview only (1,089K chars total). Download the full file or copy to clipboard to get everything.
Repository: Gabriel439/grace
Branch: main
Commit: 9230b0a0fc60
Files: 847
Total size: 897.1 KB

Directory structure:
gitextract_9bjoz56v/

├── .envrc
├── .gitattributes
├── .gitignore
├── .hlint.yaml
├── .stylish-haskell.yaml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── Setup.hs
├── dependencies/
│   ├── .gitkeep
│   ├── grace-ghcjs.nix
│   ├── grace.nix
│   └── openai.nix
├── doctest/
│   └── Main.hs
├── examples/
│   ├── bluesky/
│   │   └── 2025-11-27.ffg
│   ├── chaining.ffg
│   ├── code.ffg
│   ├── emotion-wheel.ffg
│   ├── eval.ffg
│   ├── history.ffg
│   ├── learn-in-y-minutes.ffg
│   ├── mustache.ffg
│   ├── poem.ffg
│   ├── prompt.ffg
│   ├── taxes.ffg
│   ├── tools.ffg
│   ├── transform-text.ffg
│   ├── tutorial/
│   │   ├── coding.ffg
│   │   ├── conclusion.ffg
│   │   ├── data.ffg
│   │   ├── functions.ffg
│   │   ├── hello.ffg
│   │   ├── html.ffg
│   │   ├── imports.ffg
│   │   ├── prompting.ffg
│   │   └── variables.ffg
│   └── users.ffg
├── flake.nix
├── garnix.yaml
├── ghc/
│   └── Grace/
│       ├── DataFile.hs
│       ├── HTTP.hs
│       └── Width.hs
├── ghcjs/
│   └── Grace/
│       ├── DataFile.hs
│       ├── HTTP.hs
│       ├── Size.hs
│       └── Width.hs
├── grace/
│   └── Main.hs
├── grace.cabal
├── prelude/
│   ├── bool/
│   │   ├── and.ffg
│   │   ├── not.ffg
│   │   ├── or.ffg
│   │   └── package.ffg
│   ├── integer/
│   │   ├── clamp.ffg
│   │   ├── package.ffg
│   │   ├── product.ffg
│   │   ├── sum.ffg
│   │   └── toNatural.ffg
│   ├── list/
│   │   ├── all.ffg
│   │   ├── any.ffg
│   │   ├── concat.ffg
│   │   ├── generate.ffg
│   │   ├── null.ffg
│   │   ├── package.ffg
│   │   ├── reject.ffg
│   │   ├── replicate.ffg
│   │   └── select.ffg
│   ├── natural/
│   │   ├── package.ffg
│   │   ├── product.ffg
│   │   └── sum.ffg
│   ├── package.ffg
│   ├── real/
│   │   ├── package.ffg
│   │   ├── product.ffg
│   │   └── sum.ffg
│   └── text/
│       ├── concat.ffg
│       ├── concatMap.ffg
│       ├── concatMapSep.ffg
│       ├── concatSep.ffg
│       └── package.ffg
├── prompts/
│   ├── abnf.md
│   └── inference.md
├── src/
│   ├── Grace/
│   │   ├── Aeson.hs
│   │   ├── Compat.hs
│   │   ├── Context.hs
│   │   ├── Decode.hs
│   │   ├── Domain.hs
│   │   ├── Encode.hs
│   │   ├── Existential.hs
│   │   ├── GitHub.hs
│   │   ├── HTTP/
│   │   │   └── Type.hs
│   │   ├── Import.hs
│   │   ├── Infer.hs
│   │   ├── Input.hs
│   │   ├── Interpret.hs
│   │   ├── Interpret.hs-boot
│   │   ├── Label.hs
│   │   ├── Location.hs
│   │   ├── Marshal.hs
│   │   ├── Monad.hs
│   │   ├── Monotype.hs
│   │   ├── Normalize.hs
│   │   ├── Parallelizable.hs
│   │   ├── Parser.hs
│   │   ├── Pretty.hs
│   │   ├── Prompt/
│   │   │   └── Types.hs
│   │   ├── Prompt.hs
│   │   ├── REPL.hs
│   │   ├── Syntax.hs
│   │   ├── TH.hs
│   │   ├── Type.hs
│   │   └── Value.hs
│   └── Grace.hs
├── tasty/
│   ├── Main.hs
│   └── data/
│       ├── complex/
│       │   ├── abstract-fold-input.ffg
│       │   ├── abstract-fold-output.ffg
│       │   ├── abstract-fold-type.ffg
│       │   ├── alternative-input.ffg
│       │   ├── alternative-output.ffg
│       │   ├── alternative-type.ffg
│       │   ├── arithmetic-input.ffg
│       │   ├── arithmetic-output.ffg
│       │   ├── arithmetic-type.ffg
│       │   ├── chain-identity-input.ffg
│       │   ├── chain-identity-output.ffg
│       │   ├── chain-identity-type.ffg
│       │   ├── check-function-input.ffg
│       │   ├── check-function-output.ffg
│       │   ├── check-function-type.ffg
│       │   ├── church-encode-list-input.ffg
│       │   ├── church-encode-list-output.ffg
│       │   ├── church-encode-list-type.ffg
│       │   ├── default-arguments-input.ffg
│       │   ├── default-arguments-output.ffg
│       │   ├── default-arguments-type.ffg
│       │   ├── destructure-lambda-input.ffg
│       │   ├── destructure-lambda-output.ffg
│       │   ├── destructure-lambda-type.ffg
│       │   ├── destructure-let-argument-input.ffg
│       │   ├── destructure-let-argument-output.ffg
│       │   ├── destructure-let-argument-type.ffg
│       │   ├── destructure-record-input.ffg
│       │   ├── destructure-record-output.ffg
│       │   ├── destructure-record-type.ffg
│       │   ├── elaborate-inside-optional-input.ffg
│       │   ├── elaborate-inside-optional-output.ffg
│       │   ├── elaborate-inside-optional-type.ffg
│       │   ├── equality-input.ffg
│       │   ├── equality-output.ffg
│       │   ├── equality-type.ffg
│       │   ├── even-input.ffg
│       │   ├── even-output.ffg
│       │   ├── even-type.ffg
│       │   ├── examples-input.ffg
│       │   ├── examples-output.ffg
│       │   ├── examples-type.ffg
│       │   ├── exclaim-input.ffg
│       │   ├── exclaim-output.ffg
│       │   ├── exclaim-type.ffg
│       │   ├── exhaust-type-variable-alphabet-input.ffg
│       │   ├── exhaust-type-variable-alphabet-output.ffg
│       │   ├── exhaust-type-variable-alphabet-type.ffg
│       │   ├── existential-records-input.ffg
│       │   ├── existential-records-output.ffg
│       │   ├── existential-records-type.ffg
│       │   ├── export-show-input.ffg
│       │   ├── export-show-output.ffg
│       │   ├── export-show-type.ffg
│       │   ├── extra-fields-input.ffg
│       │   ├── extra-fields-output.ffg
│       │   ├── extra-fields-type.ffg
│       │   ├── fold-bool-input.ffg
│       │   ├── fold-bool-output.ffg
│       │   ├── fold-bool-type.ffg
│       │   ├── fold-json-input.ffg
│       │   ├── fold-json-output.ffg
│       │   ├── fold-json-type.ffg
│       │   ├── fold-list-input.ffg
│       │   ├── fold-list-output.ffg
│       │   ├── fold-list-type.ffg
│       │   ├── fold-missing-field-input.ffg
│       │   ├── fold-missing-field-output.ffg
│       │   ├── fold-missing-field-type.ffg
│       │   ├── fold-natural-input.ffg
│       │   ├── fold-natural-output.ffg
│       │   ├── fold-natural-type.ffg
│       │   ├── fold-optional-input.ffg
│       │   ├── fold-optional-output.ffg
│       │   ├── fold-optional-type.ffg
│       │   ├── for-annotation-input.ffg
│       │   ├── for-annotation-output.ffg
│       │   ├── for-annotation-type.ffg
│       │   ├── for-check-list-input.ffg
│       │   ├── for-check-list-output.ffg
│       │   ├── for-check-list-type.ffg
│       │   ├── for-lambda-input.ffg
│       │   ├── for-lambda-output.ffg
│       │   ├── for-lambda-type.ffg
│       │   ├── for-list-input.ffg
│       │   ├── for-list-output.ffg
│       │   ├── for-list-type.ffg
│       │   ├── for-naked-output.ffg
│       │   ├── for-naked-type.ffg
│       │   ├── for-optional-input.ffg
│       │   ├── for-optional-output.ffg
│       │   ├── for-optional-type.ffg
│       │   ├── forall-annotation-input.ffg
│       │   ├── forall-annotation-output.ffg
│       │   ├── forall-annotation-type.ffg
│       │   ├── higher-rank-annotation-input.ffg
│       │   ├── higher-rank-annotation-output.ffg
│       │   ├── higher-rank-annotation-type.ffg
│       │   ├── higher-rank-input.ffg
│       │   ├── higher-rank-output.ffg
│       │   ├── higher-rank-subtype-input.ffg
│       │   ├── higher-rank-subtype-output.ffg
│       │   ├── higher-rank-subtype-type.ffg
│       │   ├── higher-rank-type.ffg
│       │   ├── if-then-else-input.ffg
│       │   ├── if-then-else-output.ffg
│       │   ├── if-then-else-type.ffg
│       │   ├── import-github-input.ffg
│       │   ├── import-github-output.ffg
│       │   ├── import-github-type.ffg
│       │   ├── impredicative-const-input.ffg
│       │   ├── impredicative-const-output.ffg
│       │   ├── impredicative-const-type.ffg
│       │   ├── index-input.ffg
│       │   ├── index-output.ffg
│       │   ├── index-type.ffg
│       │   ├── inequality-input.ffg
│       │   ├── inequality-output.ffg
│       │   ├── inequality-type.ffg
│       │   ├── infer-higher-order-input.ffg
│       │   ├── infer-higher-order-output.ffg
│       │   ├── infer-higher-order-type.ffg
│       │   ├── infer-reverse-input.ffg
│       │   ├── infer-reverse-output.ffg
│       │   ├── infer-reverse-type.ffg
│       │   ├── inner-forall-input.ffg
│       │   ├── inner-forall-output.ffg
│       │   ├── inner-forall-type.ffg
│       │   ├── instantiate-nested-type-input.ffg
│       │   ├── instantiate-nested-type-output.ffg
│       │   ├── instantiate-nested-type-type.ffg
│       │   ├── instantiate-variable-input.ffg
│       │   ├── instantiate-variable-output.ffg
│       │   ├── instantiate-variable-type.ffg
│       │   ├── interpolate-default-input.ffg
│       │   ├── interpolate-default-output.ffg
│       │   ├── interpolate-default-type.ffg
│       │   ├── interpolation-input.ffg
│       │   ├── interpolation-output.ffg
│       │   ├── interpolation-type.ffg
│       │   ├── json-fold-input.ffg
│       │   ├── json-fold-output.ffg
│       │   ├── json-fold-type.ffg
│       │   ├── keyword-prefix-input.ffg
│       │   ├── keyword-prefix-output.ffg
│       │   ├── keyword-prefix-type.ffg
│       │   ├── lambda-annotation-input.ffg
│       │   ├── lambda-annotation-output.ffg
│       │   ├── lambda-annotation-type.ffg
│       │   ├── let-functions-input.ffg
│       │   ├── let-functions-output.ffg
│       │   ├── let-functions-type.ffg
│       │   ├── list-append-subtype-input.ffg
│       │   ├── list-append-subtype-output.ffg
│       │   ├── list-append-subtype-type.ffg
│       │   ├── list-of-alternatives-input.ffg
│       │   ├── list-of-alternatives-output.ffg
│       │   ├── list-of-alternatives-type.ffg
│       │   ├── list-unequal-length-input.ffg
│       │   ├── list-unequal-length-output.ffg
│       │   ├── list-unequal-length-type.ffg
│       │   ├── merge-multiple-input.ffg
│       │   ├── merge-multiple-output.ffg
│       │   ├── merge-multiple-type.ffg
│       │   ├── merge-optional-input.ffg
│       │   ├── merge-optional-output.ffg
│       │   ├── merge-optional-type.ffg
│       │   ├── minus-natural-input.ffg
│       │   ├── minus-natural-output.ffg
│       │   ├── minus-natural-type.ffg
│       │   ├── modulus-input.ffg
│       │   ├── modulus-output.ffg
│       │   ├── modulus-type.ffg
│       │   ├── nested-arithmetic-input.ffg
│       │   ├── nested-arithmetic-output.ffg
│       │   ├── nested-arithmetic-type.ffg
│       │   ├── nested-constructor-input.ffg
│       │   ├── nested-constructor-output.ffg
│       │   ├── nested-constructor-type.ffg
│       │   ├── nested-fields-input.ffg
│       │   ├── nested-fields-output.ffg
│       │   ├── nested-fields-type.ffg
│       │   ├── nested-let-input.ffg
│       │   ├── nested-let-output.ffg
│       │   ├── nested-let-type.ffg
│       │   ├── numeric-subtyping-input.ffg
│       │   ├── numeric-subtyping-output.ffg
│       │   ├── numeric-subtyping-type.ffg
│       │   ├── optional-fields-input.ffg
│       │   ├── optional-fields-output.ffg
│       │   ├── optional-fields-type.ffg
│       │   ├── package-input.ffg
│       │   ├── package-output.ffg
│       │   ├── package-type.ffg
│       │   ├── pattern-input.ffg
│       │   ├── pattern-output.ffg
│       │   ├── pattern-type.ffg
│       │   ├── polymorphic-instantiation-input.ffg
│       │   ├── polymorphic-instantiation-output.ffg
│       │   ├── polymorphic-instantiation-type.ffg
│       │   ├── polymorphic-let-input.ffg
│       │   ├── polymorphic-let-output.ffg
│       │   ├── polymorphic-let-type.ffg
│       │   ├── polymorphic-subsumption-input.ffg
│       │   ├── polymorphic-subsumption-output.ffg
│       │   ├── polymorphic-subsumption-type.ffg
│       │   ├── potpourri-input.ffg
│       │   ├── potpourri-output.ffg
│       │   ├── potpourri-type.ffg
│       │   ├── predicative-null-input.ffg
│       │   ├── predicative-null-output.ffg
│       │   ├── predicative-null-type.ffg
│       │   ├── prelude-input.ffg
│       │   ├── prelude-output.ffg
│       │   ├── prelude-type.ffg
│       │   ├── preserve-row-input.ffg
│       │   ├── preserve-row-output.ffg
│       │   ├── preserve-row-type.ffg
│       │   ├── principle-of-explosion-input.ffg
│       │   ├── principle-of-explosion-output.ffg
│       │   ├── principle-of-explosion-type.ffg
│       │   ├── quantifiers-input.ffg
│       │   ├── quantifiers-output.ffg
│       │   ├── quantifiers-type.ffg
│       │   ├── quoted-label-input.ffg
│       │   ├── quoted-label-output.ffg
│       │   ├── quoted-label-type.ffg
│       │   ├── read-input.ffg
│       │   ├── read-output.ffg
│       │   ├── read-type.ffg
│       │   ├── record-mismatch-both-extra-input.ffg
│       │   ├── record-mismatch-both-extra-output.ffg
│       │   ├── record-mismatch-both-extra-type.ffg
│       │   ├── record-mismatch-left-extra-input.ffg
│       │   ├── record-mismatch-left-extra-output.ffg
│       │   ├── record-mismatch-left-extra-type.ffg
│       │   ├── record-mismatch-right-extra-input.ffg
│       │   ├── record-mismatch-right-extra-output.ffg
│       │   ├── record-mismatch-right-extra-type.ffg
│       │   ├── record-mismatch-two-extra-input.ffg
│       │   ├── record-mismatch-two-extra-output.ffg
│       │   ├── record-mismatch-two-extra-type.ffg
│       │   ├── remote-transitive-import-input.ffg
│       │   ├── remote-transitive-import-output.ffg
│       │   ├── remote-transitive-import-type.ffg
│       │   ├── restrict-project-input.ffg
│       │   ├── restrict-project-output.ffg
│       │   ├── restrict-project-type.ffg
│       │   ├── separators-input.ffg
│       │   ├── separators-output.ffg
│       │   ├── separators-type.ffg
│       │   ├── show-input.ffg
│       │   ├── show-output.ffg
│       │   ├── show-type.ffg
│       │   ├── skolem-function-type-input.ffg
│       │   ├── skolem-function-type-stderr.txt
│       │   ├── slice-input.ffg
│       │   ├── slice-output.ffg
│       │   ├── slice-type.ffg
│       │   ├── solve-fields-input.ffg
│       │   ├── solve-fields-output.ffg
│       │   ├── solve-fields-type.ffg
│       │   ├── solve-unused-impredicative-polymorphism-input.ffg
│       │   ├── solve-unused-impredicative-polymorphism-output.ffg
│       │   ├── solve-unused-impredicative-polymorphism-type.ffg
│       │   ├── subtype-function-input.ffg
│       │   ├── subtype-function-output.ffg
│       │   ├── subtype-function-type.ffg
│       │   ├── triple-field-input.ffg
│       │   ├── triple-field-output.ffg
│       │   ├── triple-field-type.ffg
│       │   ├── triple-supertype-input.ffg
│       │   ├── triple-supertype-output.ffg
│       │   ├── triple-supertype-type.ffg
│       │   ├── unbound-alternatives-variable-input.ffg
│       │   ├── unbound-alternatives-variable-stderr.txt
│       │   ├── unbound-fields-variable-input.ffg
│       │   ├── unbound-fields-variable-stderr.txt
│       │   ├── undo-elaboration-input.ffg
│       │   ├── undo-elaboration-output.ffg
│       │   ├── undo-elaboration-type.ffg
│       │   ├── unify-json-input.ffg
│       │   ├── unify-json-output.ffg
│       │   ├── unify-json-type.ffg
│       │   ├── unify-null-input.ffg
│       │   ├── unify-null-output.ffg
│       │   ├── unify-null-type.ffg
│       │   ├── unify-unsolved-input.ffg
│       │   ├── unify-unsolved-output.ffg
│       │   ├── unify-unsolved-type.ffg
│       │   ├── unused-impredicative-polymorphism-input.ffg
│       │   ├── unused-impredicative-polymorphism-output.ffg
│       │   ├── unused-impredicative-polymorphism-type.ffg
│       │   ├── unused-polymorphism-input.ffg
│       │   ├── unused-polymorphism-output.ffg
│       │   ├── unused-polymorphism-type.ffg
│       │   ├── use-equation-input.ffg
│       │   ├── use-equation-output.ffg
│       │   ├── use-equation-type.ffg
│       │   ├── well-formed-input.ffg
│       │   ├── well-formed-output.ffg
│       │   ├── well-formed-type.ffg
│       │   ├── yaml-input.ffg
│       │   ├── yaml-output.ffg
│       │   └── yaml-type.ffg
│       ├── error/
│       │   ├── for-naked-input.ffg
│       │   ├── for-naked-stderr.txt
│       │   ├── parse/
│       │   │   ├── eof-input.ffg
│       │   │   ├── eof-stderr.txt
│       │   │   ├── incomplete-escape-input.ffg
│       │   │   ├── incomplete-escape-stderr.txt
│       │   │   ├── incomplete-interpolation-input.ffg
│       │   │   ├── incomplete-interpolation-stderr.txt
│       │   │   ├── incomplete-string-input.ffg
│       │   │   ├── incomplete-string-stderr.txt
│       │   │   ├── invalid-multiline-input.ffg
│       │   │   ├── invalid-multiline-stderr.txt
│       │   │   ├── unbalanced-bracket-input.ffg
│       │   │   ├── unbalanced-bracket-stderr.txt
│       │   │   ├── unrecognized-symbol-input.ffg
│       │   │   └── unrecognized-symbol-stderr.txt
│       │   ├── resolve/
│       │   │   ├── empty-environment-variable-input.ffg
│       │   │   ├── empty-environment-variable-stderr.txt
│       │   │   ├── environment-authority-input.ffg
│       │   │   ├── environment-authority-stderr.txt
│       │   │   ├── environment-leading-slash-input.ffg
│       │   │   ├── environment-leading-slash-stderr.txt
│       │   │   ├── environment-missing-input.ffg
│       │   │   ├── environment-missing-stderr.txt
│       │   │   ├── environment-variable-slash-input.ffg
│       │   │   ├── environment-variable-slash-stderr.txt
│       │   │   ├── file-authority-input.ffg
│       │   │   ├── file-authority-stderr.txt
│       │   │   ├── file-missing-path-input.ffg
│       │   │   ├── file-missing-path-stderr.txt
│       │   │   ├── host-not-found-input.ffg
│       │   │   ├── host-not-found-stderr.txt
│       │   │   ├── invalid-destination-host-input.ffg
│       │   │   ├── invalid-destination-host-stderr.txt
│       │   │   ├── invalid-utf8-input.ffg
│       │   │   ├── invalid-utf8-stderr.txt
│       │   │   ├── read-invalid-json-input.ffg
│       │   │   ├── read-invalid-json-stderr.txt
│       │   │   ├── remote-invalid-parse-input.ffg
│       │   │   ├── remote-invalid-parse-stderr.txt
│       │   │   ├── remote-referentially-insane-input.ffg
│       │   │   └── remote-referentially-insane-stderr.txt
│       │   └── type/
│       │       ├── alternatives-variable-input.ffg
│       │       ├── alternatives-variable-stderr.txt
│       │       ├── bad-type-annotation-input.ffg
│       │       ├── bad-type-annotation-stderr.txt
│       │       ├── destructure-missing-required-input.ffg
│       │       ├── destructure-missing-required-stderr.txt
│       │       ├── dynamic-scope-input.ffg
│       │       ├── dynamic-scope-stderr.txt
│       │       ├── fields-variable-mismatch-input.ffg
│       │       ├── fields-variable-mismatch-stderr.txt
│       │       ├── fold-missing-field-input.ffg
│       │       ├── fold-missing-field-stderr.txt
│       │       ├── fold-typo-field-input.ffg
│       │       ├── fold-typo-field-stderr.txt
│       │       ├── for-annotation-input.ffg
│       │       ├── for-annotation-stderr.txt
│       │       ├── for-mixed-input.ffg
│       │       ├── for-mixed-stderr.txt
│       │       ├── impredicative-instantiation-input.ffg
│       │       ├── impredicative-instantiation-stderr.txt
│       │       ├── impredicative-polymorphism-input.ffg
│       │       ├── impredicative-polymorphism-output.ffg
│       │       ├── impredicative-polymorphism-type.ffg
│       │       ├── index-slice-input.ffg
│       │       ├── index-slice-stderr.txt
│       │       ├── invalid-argument-input.ffg
│       │       ├── invalid-argument-stderr.txt
│       │       ├── invalid-handler-input.ffg
│       │       ├── invalid-handler-stderr.txt
│       │       ├── invalid-plus-annotation-input.ffg
│       │       ├── invalid-plus-annotation-stderr.txt
│       │       ├── invalid-plus-operands-input.ffg
│       │       ├── invalid-plus-operands-stderr.txt
│       │       ├── invalid-times-annotation-input.ffg
│       │       ├── invalid-times-annotation-stderr.txt
│       │       ├── invalid-times-operands-input.ffg
│       │       ├── invalid-times-operands-stderr.txt
│       │       ├── merge-wrong-type-input.ffg
│       │       ├── merge-wrong-type-stderr.txt
│       │       ├── missing-required-field-input.ffg
│       │       ├── missing-required-field-stderr.txt
│       │       ├── modulus-abstract-input.ffg
│       │       ├── modulus-abstract-stderr.txt
│       │       ├── modulus-zero-input.ffg
│       │       ├── modulus-zero-stderr.txt
│       │       ├── non-singular-pattern-input.ffg
│       │       ├── non-singular-pattern-stderr.txt
│       │       ├── not-a-function-input.ffg
│       │       ├── not-a-function-stderr.txt
│       │       ├── occurs-alternatives-input.ffg
│       │       ├── occurs-alternatives-stderr.txt
│       │       ├── occurs-type-backwards-input.ffg
│       │       ├── occurs-type-backwards-stderr.txt
│       │       ├── occurs-type-input.ffg
│       │       ├── occurs-type-stderr.txt
│       │       ├── pattern-input.ffg
│       │       ├── pattern-stderr.txt
│       │       ├── pin-fields-variable-input.ffg
│       │       ├── pin-fields-variable-stderr.txt
│       │       ├── read-fail-input.ffg
│       │       ├── read-fail-stderr.txt
│       │       ├── read-input.ffg
│       │       ├── read-stderr.txt
│       │       ├── show-key-input.ffg
│       │       ├── show-key-stderr.txt
│       │       ├── subtype-function-input.ffg
│       │       ├── subtype-function-stderr.txt
│       │       ├── type-mismatch-input.ffg
│       │       ├── type-mismatch-stderr.txt
│       │       ├── unbound-input.ffg
│       │       ├── unbound-stderr.txt
│       │       ├── unbound-type-input.ffg
│       │       ├── unbound-type-stderr.txt
│       │       ├── union-mismatch-both-extra-input.ffg
│       │       ├── union-mismatch-both-extra-stderr.txt
│       │       ├── union-mismatch-left-extra-input.ffg
│       │       ├── union-mismatch-left-extra-stderr.txt
│       │       ├── union-mismatch-right-extra-input.ffg
│       │       ├── union-mismatch-right-extra-output.ffg
│       │       ├── union-mismatch-right-extra-type.ffg
│       │       ├── union-variable-mismatch-input.ffg
│       │       ├── union-variable-mismatch-stderr.txt
│       │       ├── variable-mismatch-input.ffg
│       │       └── variable-mismatch-stderr.txt
│       ├── json/
│       │   ├── colors-input.ffg
│       │   ├── colors-output.ffg
│       │   ├── colors-type.ffg
│       │   ├── colors.json
│       │   ├── geoip-input.ffg
│       │   ├── geoip-output.ffg
│       │   ├── geoip-type.ffg
│       │   ├── geoip.json
│       │   ├── glossary-input.ffg
│       │   ├── glossary-output.ffg
│       │   ├── glossary-type.ffg
│       │   ├── glossary.json
│       │   ├── markers-input.ffg
│       │   ├── markers-output.ffg
│       │   ├── markers-type.ffg
│       │   ├── markers.json
│       │   ├── menu-input.ffg
│       │   ├── menu-output.ffg
│       │   ├── menu-type.ffg
│       │   ├── menu.json
│       │   ├── menu2-input.ffg
│       │   ├── menu2-output.ffg
│       │   ├── menu2-type.ffg
│       │   ├── menu2.json
│       │   ├── numbers-input.ffg
│       │   ├── numbers-output.ffg
│       │   ├── numbers-type.ffg
│       │   ├── numbers.json
│       │   ├── people-input.ffg
│       │   ├── people-output.ffg
│       │   ├── people-type.ffg
│       │   ├── people.json
│       │   ├── products-input.ffg
│       │   ├── products-output.ffg
│       │   ├── products-type.ffg
│       │   ├── products.json
│       │   ├── recipes-input.ffg
│       │   ├── recipes-output.ffg
│       │   ├── recipes-type.ffg
│       │   ├── recipes.json
│       │   ├── server-input.ffg
│       │   ├── server-output.ffg
│       │   ├── server-type.ffg
│       │   ├── server.json
│       │   ├── twitter-input.ffg
│       │   ├── twitter-output.ffg
│       │   ├── twitter-type.ffg
│       │   ├── twitter.json
│       │   ├── widget-input.ffg
│       │   ├── widget-output.ffg
│       │   ├── widget-type.ffg
│       │   ├── widget.json
│       │   ├── youtube-input.ffg
│       │   ├── youtube-output.ffg
│       │   ├── youtube-type.ffg
│       │   └── youtube.json
│       ├── true.ffg
│       └── unit/
│           ├── add-integer-input.ffg
│           ├── add-integer-output.ffg
│           ├── add-integer-type.ffg
│           ├── alternative-input.ffg
│           ├── alternative-output.ffg
│           ├── alternative-type.ffg
│           ├── and-input.ffg
│           ├── and-output.ffg
│           ├── and-type.ffg
│           ├── append-list-input.ffg
│           ├── append-list-output.ffg
│           ├── append-list-type.ffg
│           ├── append-text-input.ffg
│           ├── append-text-output.ffg
│           ├── append-text-type.ffg
│           ├── application-input.ffg
│           ├── application-output.ffg
│           ├── application-type.ffg
│           ├── bool-input.ffg
│           ├── bool-output.ffg
│           ├── bool-type.ffg
│           ├── default-argument-input.ffg
│           ├── default-argument-output.ffg
│           ├── default-argument-type.ffg
│           ├── default-record-argument-input.ffg
│           ├── default-record-argument-output.ffg
│           ├── default-record-argument-type.ffg
│           ├── destructure-lambda-input.ffg
│           ├── destructure-lambda-output.ffg
│           ├── destructure-lambda-type.ffg
│           ├── destructure-let-argument-input.ffg
│           ├── destructure-let-argument-output.ffg
│           ├── destructure-let-argument-type.ffg
│           ├── destructure-record-input.ffg
│           ├── destructure-record-output.ffg
│           ├── destructure-record-type.ffg
│           ├── divide-input.ffg
│           ├── divide-output.ffg
│           ├── divide-type.ffg
│           ├── equal-input.ffg
│           ├── equal-output.ffg
│           ├── equal-type.ffg
│           ├── export-show-input.ffg
│           ├── export-show-output.ffg
│           ├── export-show-type.ffg
│           ├── field-input.ffg
│           ├── field-output.ffg
│           ├── field-type.ffg
│           ├── fields-input.ffg
│           ├── fields-output.ffg
│           ├── fields-type.ffg
│           ├── for-list-input.ffg
│           ├── for-list-output.ffg
│           ├── for-list-type.ffg
│           ├── for-optional-input.ffg
│           ├── for-optional-output.ffg
│           ├── for-optional-type.ffg
│           ├── greater-than-input.ffg
│           ├── greater-than-or-equal-input.ffg
│           ├── greater-than-or-equal-output.ffg
│           ├── greater-than-or-equal-type.ffg
│           ├── greater-than-output.ffg
│           ├── greater-than-type.ffg
│           ├── if-input.ffg
│           ├── if-output.ffg
│           ├── if-type.ffg
│           ├── import-input.ffg
│           ├── import-output.ffg
│           ├── import-type.ffg
│           ├── index-input.ffg
│           ├── index-output.ffg
│           ├── index-type.ffg
│           ├── inequality-input.ffg
│           ├── inequality-output.ffg
│           ├── inequality-type.ffg
│           ├── integer-abs-input.ffg
│           ├── integer-abs-output.ffg
│           ├── integer-abs-type.ffg
│           ├── integer-input.ffg
│           ├── integer-output.ffg
│           ├── integer-type.ffg
│           ├── interpolation-input.ffg
│           ├── interpolation-output.ffg
│           ├── interpolation-type.ffg
│           ├── json-input.ffg
│           ├── json-output.ffg
│           ├── json-subtype-input.ffg
│           ├── json-subtype-output.ffg
│           ├── json-subtype-type.ffg
│           ├── json-type.ffg
│           ├── key-input.ffg
│           ├── key-output.ffg
│           ├── key-type.ffg
│           ├── lambda-annotation-input.ffg
│           ├── lambda-annotation-output.ffg
│           ├── lambda-annotation-type.ffg
│           ├── lambda-input.ffg
│           ├── lambda-output.ffg
│           ├── lambda-type.ffg
│           ├── less-than-input.ffg
│           ├── less-than-or-equal-input.ffg
│           ├── less-than-or-equal-output.ffg
│           ├── less-than-or-equal-type.ffg
│           ├── less-than-output.ffg
│           ├── less-than-type.ffg
│           ├── let-default-input.ffg
│           ├── let-default-output.ffg
│           ├── let-default-type.ffg
│           ├── let-function-input.ffg
│           ├── let-function-output.ffg
│           ├── let-function-type.ffg
│           ├── let-input.ffg
│           ├── let-output.ffg
│           ├── let-type.ffg
│           ├── list-fold-input.ffg
│           ├── list-fold-output.ffg
│           ├── list-fold-type.ffg
│           ├── list-indexed-input.ffg
│           ├── list-indexed-output.ffg
│           ├── list-indexed-type.ffg
│           ├── list-input.ffg
│           ├── list-length-input.ffg
│           ├── list-length-output.ffg
│           ├── list-length-type.ffg
│           ├── list-map-input.ffg
│           ├── list-map-output.ffg
│           ├── list-map-type.ffg
│           ├── list-output.ffg
│           ├── list-type.ffg
│           ├── merge-bool-input.ffg
│           ├── merge-bool-output.ffg
│           ├── merge-bool-type.ffg
│           ├── merge-evaluation-input.ffg
│           ├── merge-evaluation-output.ffg
│           ├── merge-evaluation-type.ffg
│           ├── merge-input.ffg
│           ├── merge-optional-input.ffg
│           ├── merge-optional-output.ffg
│           ├── merge-optional-type.ffg
│           ├── merge-output.ffg
│           ├── merge-type.ffg
│           ├── minus-input.ffg
│           ├── minus-output.ffg
│           ├── minus-type.ffg
│           ├── missing-field-input.ffg
│           ├── missing-field-output.ffg
│           ├── missing-field-type.ffg
│           ├── missing-optional-field-input.ffg
│           ├── missing-optional-field-output.ffg
│           ├── missing-optional-field-type.ffg
│           ├── modulus-input.ffg
│           ├── modulus-output.ffg
│           ├── modulus-type.ffg
│           ├── multi-lambda-input.ffg
│           ├── multi-lambda-output.ffg
│           ├── multi-lambda-type.ffg
│           ├── multiline-input.ffg
│           ├── multiline-output.ffg
│           ├── multiline-type.ffg
│           ├── multiple-path-components-input.ffg
│           ├── multiple-path-components-output.ffg
│           ├── multiple-path-components-type.ffg
│           ├── natural-fold-input.ffg
│           ├── natural-fold-output.ffg
│           ├── natural-fold-type.ffg
│           ├── natural-input.ffg
│           ├── natural-output.ffg
│           ├── natural-type.ffg
│           ├── not-equal-input.ffg
│           ├── not-equal-output.ffg
│           ├── not-equal-type.ffg
│           ├── null-input.ffg
│           ├── null-output.ffg
│           ├── null-type.ffg
│           ├── or-input.ffg
│           ├── or-output.ffg
│           ├── or-type.ffg
│           ├── pattern-input.ffg
│           ├── pattern-output.ffg
│           ├── pattern-type.ffg
│           ├── plus-input.ffg
│           ├── plus-output.ffg
│           ├── plus-type.ffg
│           ├── positive-integer-input.ffg
│           ├── positive-integer-output.ffg
│           ├── positive-integer-type.ffg
│           ├── pun-input.ffg
│           ├── pun-output.ffg
│           ├── pun-type.ffg
│           ├── quoted-label-input.ffg
│           ├── quoted-label-output.ffg
│           ├── quoted-label-type.ffg
│           ├── read-input.ffg
│           ├── read-output.ffg
│           ├── read-type.ffg
│           ├── real-dot-zero-input.ffg
│           ├── real-dot-zero-output.ffg
│           ├── real-dot-zero-type.ffg
│           ├── real-input.ffg
│           ├── real-output.ffg
│           ├── real-type.ffg
│           ├── record-desugar-input.ffg
│           ├── record-desugar-output.ffg
│           ├── record-desugar-type.ffg
│           ├── record-input.ffg
│           ├── record-output.ffg
│           ├── record-type.ffg
│           ├── remote-import-input.ffg
│           ├── remote-import-output.ffg
│           ├── remote-import-type.ffg
│           ├── reveal-input.ffg
│           ├── reveal-output.ffg
│           ├── reveal-type.ffg
│           ├── show-input.ffg
│           ├── show-output.ffg
│           ├── show-type.ffg
│           ├── slice-input.ffg
│           ├── slice-output.ffg
│           ├── slice-type.ffg
│           ├── string-labels-input.ffg
│           ├── string-labels-output.ffg
│           ├── string-labels-type.ffg
│           ├── text-escaped-input.ffg
│           ├── text-escaped-output.ffg
│           ├── text-escaped-type.ffg
│           ├── text-input.ffg
│           ├── text-output.ffg
│           ├── text-type.ffg
│           ├── times-input.ffg
│           ├── times-output.ffg
│           ├── times-type.ffg
│           ├── union-input.ffg
│           ├── union-output.ffg
│           ├── union-type.ffg
│           ├── visible-row-quantification-input.ffg
│           ├── visible-row-quantification-output.ffg
│           ├── visible-row-quantification-type.ffg
│           ├── visible-variant-quantification-input.ffg
│           ├── visible-variant-quantification-output.ffg
│           ├── visible-variant-quantification-type.ffg
│           ├── yaml-input.ffg
│           ├── yaml-output.ffg
│           └── yaml-type.ffg
├── try-grace/
│   └── Main.hs
└── website/
    ├── css/
    │   └── grace.css
    └── index.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .envrc
================================================
use flake


================================================
FILE: .gitattributes
================================================
*.ffg linguist-language=Haskell


================================================
FILE: .gitignore
================================================
dist
dist-*
cabal-dev
*.o
*.hi
*.hie
*.chi
*.chs.h
*.dyn_o
*.dyn_hi
.hpc
.hsenv
.cabal-sandbox/
cabal.sandbox.config
*.prof
*.aux
*.hp
*.eventlog
.stack-work/
cabal.project.local
cabal.project.local~
.ghc.environment.*
website/js/all.min.js
.direnv
.history
result


================================================
FILE: .hlint.yaml
================================================
- ignore: {name: Use fmap}
- ignore: {name: Use <$>}
- ignore: {name: Avoid lambda using `infix`}
- ignore: {name: Use infix}
- ignore: {name: Use tuple-section}
- ignore: {name: Avoid lambda}
- ignore: {name: Unused LANGUAGE pragma}
- ignore: {name: Use const}
- ignore: {name: Redundant bracket, within: Grace.Parser}
- ignore: {name: Redundant return, within: Grace.Parser}


================================================
FILE: .stylish-haskell.yaml
================================================
steps:
  - module_header:
      open_bracket: next_line
      separate_lists: false
      sort: false
  - imports:
      align: group
      pad_module_names: false
      long_list_align: new_line_multiline
      separate_lists: false
  - language_pragmas:
      align: true


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
Gabriel439@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Grace

This document explains how to edit, build, and run this project, both if you
want to change your fork of the language or if you want to upstream improvements
to the original language.

The easiest way to build the project is using `cabal`, and the most commonly
used commands are:

```bash
$ cabal build exe:grace                    # Build the `grace` executable
$ cabal test                               # Run all tests
$ cabal test tasty                         # Faster: run only tasty tests
$ cabal test tasty --test-option=--accept  # Update golden tests
$ cabal test doctest                       # Run only doctests
$ cabal haddock --hyperlink-source         # Generate Haskell documentation
```

You can also enable coverage checking by running this step before running the
tests:

```bash
$ cabal configure --enable-coverage
```

You'll probably also want to use [`ghcid`](https://github.com/ndmitchell/ghcid)
or
[`haskell-language-server`](https://github.com/haskell/haskell-language-server)
for interactive development.

This project also provides `devShells` for Nix users, but it's not necessary
for project development.

The project tries to be as maintainable possible, meaning that most mistakes
you make will be caught by the type-checker and you will only need to update a
few places to make the most common changes.  However, there are a few places
that you need to remember to update and the type-checker won't remind you.

For example, any time you add a new language feature you will need to update
the parser in order to actually use the feature, and nothing will automatically
remind you to do that.

Generally speaking, if you're not sure where to begin then start by identifying
the most closely-related language feature and searching the codebase for all
occurrences of the matching constructor in the [`Syntax`][syntax] tree.

## GHCJS instructions

If you want to make changes to the website you will need to build using GHCJS,
which entails the following commands:

```bash
$ nix develop .#ghcjs
[nix-shell]$ cabal v1-configure --ghcjs --disable-tests
[nix-shell]$ cabal v1-build
```

… and if you want to test the website, then run the following additional
command after each build:

```bash
[nix-shell]$ cp dist/build/try-grace/try-grace.jsexe/all.js website/js/all.min.js
```

… and then open [`./website/index.html`](./website/index.html) in your
browser.

The test suite will not work and also `ghcid` will not work when developing
using GHCJS.

## Adding a new built-in function

To add a new built-in function, edit the [`Syntax`][syntax] module to add a new
constructor to the `Builtin` type, then fix all of the type errors, which should
consist of:

* Specifying the type of the builtin
* Specifying how to pretty-print the builtin

Then, edit the [`Normalize`][normalize] module to change the `apply` function to
add a case for handling the newly-added builtin.

Finally, add support for parsing the built-in to the [`Parser`][parser] module by:

* Adding a new `Token` for the `Builtin`
* Adding the built-in name to the `reserved` words
* Adding a new parsing rule.

## Adding a new operator

Adding a new operator is basically the same as adding a new built-in, with
the main change being that you change the `Operator` type within the
[`Syntax`][syntax] module, instead of changing the `Builtin` type.

The other difference is that you will change the [`Normalize`][normalize] in a
different place (where all of the operator logic is).

## Adding a new scalar literal and the corresponding scalar type

To add a new scalar type, edit the [`Syntax`][syntax] module to add a new
constructor to the `Scalar` type (representing the scalar literal).  Also, edit
the [`Monotype`][monotype] module to add a new constructor to the `Scalar` type
in that module (representing the corresponding scalar type).  Then fix all of
the type errors, which will consist of:

* Specifying how to prettyprint the scalar literal
* Specifying how to prettyprint the scalar type
* Specifying how to infer the type of the scalar literal

  … by returning the matching scalar type you just created

Finally, edit the [`Parser`][parser] module to lex and
parse the new scalar literal and scalar type you just created.

## Adding a new keyword

Probably the easiest way to add a new keyword is to study how an existing
keyword is implemented, such as the `if` / `then` / `else` keyword.  Search
the codebase for all occurrences of the `If` constructor and follow the pattern.

## Adding a new complex type

Just like keywords, the easiest way to add a new complex type is to study how an
existing complex type is implemented, such as the `List` type.  Search the
codebase for all occurrences of the `List` constructor and follow the pattern.

## Pretty-printing

When adding new keywords or complex type you will need to take care to remember
to update the pretty-printing logic.  By default, the code will go into an
infinite loop if you forget to do this, and this post explains the reason why:

* [Pretty-print syntax trees with this one simple trick](https://www.haskellforall.com/2020/11/pretty-print-syntax-trees-with-this-one.html)

You don't need to worry about this if you are adding new built-ins / operators /
scalars, since those are already handled uniformly.

## Bidirectional type-checking

This is probably the hardest part of making any changes, especially changes that
add new keywords or complex types, since they cannot be handled uniformly.

If you're new to logical notation in general, then I recommend first reading
[A tutorial implementation of a dependently typed lambda calculus](https://www.andres-loeh.de/LambdaPi/LambdaPi.pdf),
which explains the correspondence between logical notation and Haskell code.

Then read the
[Complete and Easy Bidirectional Typechecking for Higher-Rank Polymorphism](https://arxiv.org/abs/1306.6032)
paper which explains the general principles behind the type-checking algorithm.
However, there are a few nuances that are not obvious from a casual reading of
the paper, so follow up by reading the [`Infer`][infer] module, which is heavily
commented with things I had to figure out in the course of attempting to
implement the paper.

[infer]: ./src/Grace/Infer.hs
[monotype]: ./src/Grace/Monotype.hs
[normalize]: ./src/Grace/Normalize.hs
[parser]: ./src/Grace/Parser.hs
[syntax]: ./src/Grace/Syntax.hs


================================================
FILE: LICENSE
================================================
Copyright (c) 2021 Gabriella Gonzalez
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the author nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
# Grace

[![built with garnix](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgarnix.io%2Fapi%2Fbadges%2FGabriella439%2Fgrace)](https://garnix.io/repo/Gabriella439/grace)
[![BlueSky | @trygrace.dev](https://img.shields.io/badge/Bluesky-@trygrace.dev-blue)](https://bsky.app/profile/trygrace.dev)

Grace (short for [Fall-from-Grace](#name)) is a domain-specific programming
language for prompting models.  In particular, Grace is well-suited for building
and auto-generating elaborate prompt chains

You can use Grace in your browser without installing anything by visiting
[trygrace.dev](https://trygrace.dev/).  That website includes an interactive
tutorial and is the recommended way to both learn and get started with using
Grace.

## Features

- Integrated language support for models

  You don't need to install any dependencies or import anything to get started.
  Everything you need is built directly into the language.

  The language provides a built-in `prompt` function for prompting a model:

  ```haskell
  >>> let key = ./openai.key : Key

  >>> prompt{ key, text: "Generate a list of names" }
  "
  Here are 40 varied first names (mixed genders and cultures):

  - Aiden
  - Sofia
  - Mateo
  …
  - Mabel
  - Imani
  - Zane

  Want names filtered by gender, culture, style (modern/vintage), or as full names/surnames?"
  ```

  … and you can structure the output by giving a type annotation:

  ```haskell
  >>> prompt{ key, text: "Generate a list of names" } : List Text
  [ "Ava Thompson"
  , "Liam Patel"
  , "Sophia Martinez"
  …
  , "Jackson Rivera"
  , "Zoe Wilson"
  , "Aiden Park"
  ]
  ```

  If the type is sufficiently self-explanatory, you can even omit the prompt:

  ```haskell
  >>> prompt{ key } : List { name: Text }
  [ { "name": "Alice" }
  , { "name": "Bob" }
  , { "name": "Charlie" }
  , { "name": "Diana" }
  , { "name": "Evan" }
  ]
  ```

  In fact, you can omit the type, too, if the type can be inferred from use:

  ```haskell
  >>> for { name } of prompt{ key } in "Hello, ${name}!"
  [ "Hello, Alice!"
  , "Hello, Bob!"
  , "Hello, Carol!"
  , "Hello, Dave!"
  , "Hello, Eve!"
  ]
  ```

- JSON schemas inferred from use

  That last example works even without a prompt, schema, or type because Grace's
  type checker reasons backwards from how the output is used to infer the
  correct JSON schem, like this:

  - the type checker infers that the `name` variable must be `Text`

    … because the `name` variable is interpolated into `"Hello, ${name}!"`

  - the type checker infers that the `prompt` function must generate a `List`

    … because the program loops over the output using a `for … of` loop.

  - the type checker infers each element of the `List` has type `{ name: Text }`

    … because the `for … of` loop destructures each element using `{ name }`

  - therefore the `prompt` function outputs a value of type `List{ name: Text }`

  … which you can read as "a `List` of records, each of which has a `name` field
  containing `Text`".

  The interpreter then converts that Grace type into the following matching JSON
  schema to constrain the model's output:

  ```json
  {
    "type": "array",
    "items": {
      "type": "object",
      "properties": {
        "name": {
          "type": "string"
        }
      },
      "required": ["name"],
      "additionalProperties": false
    }
  }
  ```

  Finally, the model infers from that JSON schema alone (without any additional
  prompt) that it should generate a JSON-encoded list of names.

- Code generation

  You can prefix the `prompt` keyword with `import` to ask the model to generate
  a Grace expression of any type.  For example:

  ```haskell
  >>> import prompt{ key, text: "increment" }
  \n -> n + 1
  ```

  You can use an explicit type annotation to guide the generated code:

  ```haskell
  >>> import prompt{ key, text: "increment" } : { input: Natural } -> { output: Natural }
  \{ input } -> { "output": input + 1 }
  ```

  … and if the type is informative enough then you can omit the prompt:

  ```haskell
  >>> import prompt{ key } : { "Job Description": Text } -> { "Is Finance?": Bool }
  let key = 🔒

  in  \{ "Job Description" } ->
        prompt
          { "key":
              key
          , "text":
              "
              Determine whether the following job description is for a finance role.
              Return a JSON object with a single boolean field \"Is Finance?\": true if it is a finance role, otherwise false.
              Answer only valid JSON, nothing else.
  
              Job description:
              ${.'Job Description'}
              "
          , "model":
              null
          , "search":
              null
          , "effort":
              null
          }
          : { "Is Finance?": Bool }
  ```

  Notice in that last example how the model can generate code which itself
  `prompt`s an model.  Neat!

  Inferred types also guide the code generation process, too!

  ```haskell
  >>> let upper = import prompt{ key, text: "uppercase" } in "Hello, ${upper "gabby"}!"
  "Hello, GABBY!"
  ```

  There the model infers that the type of the `upper` function needs to be
  `Text -> Text` (a function whose input is `Text` and whose output is `Text`)
  and generates an function matching that type which uppercases `Text`.

### Command line

This Haskell package builds a `grace` executable with the following command-line
API:

```bash
$ grace --help
Usage: grace COMMAND
  Command-line utility for the Grace language

Available options:
  -h,--help                Show this help text

Available commands:
  interpret                Interpret a Grace file
  text                     Render a Grace text literal
  format                   Format Grace code
  builtins                 List all built-in functions and their types
  repl                     Enter a REPL for Grace
```

You can use the `interpret` subcommand for interpreting a single file:

```haskell
# ./greet.ffg

for { name } of prompt{ key: ./openai-key.txt }

in  "Hello, ${name}!"
```

```bash
$ grace interpret ./greet.ffg
```
```json
[ "Hello, Alice!", "Hello, Bob!", "Hello, Charlie!" ]
```

… and you can specify `-` to process standard input instead of a file, like
this:

```bash
$ grace interpret - <<< '2 + 2'
```
```haskell
4
```

You can also use the `repl` subcommand for interactive usage:

```bash
$ grace repl
```
```haskell
>>> let key = ./openai-key.txt
>>> prompt{ key } : List { "First Name": Text, "Last Name": Text }
[ { "First Name": "John", "Last Name": "Doe" }
, { "First Name": "Jane", "Last Name": "Smith" }
, { "First Name": "Alice", "Last Name": "Johnson" }
, { "First Name": "Michael", "Last Name": "Brown" }
, { "First Name": "Emily", "Last Name": "Davis" }
]
```

## Name

Like all of my programming language projects, Grace is named after a character
from PlaneScape: Torment, specifically
[Fall-from-Grace](https://torment.fandom.com/wiki/Fall-from-Grace).

The name of this programming language conflicts with
[another programming language](http://gracelang.org/applications/), so use the
longer name, "Fall-from-Grace", to disambiguate when it's not clear from the
context.


================================================
FILE: Setup.hs
================================================
import qualified Distribution.Simple

main :: IO ()
main = Distribution.Simple.defaultMain


================================================
FILE: dependencies/.gitkeep
================================================


================================================
FILE: dependencies/grace-ghcjs.nix
================================================
{ mkDerivation, aeson, aeson-pretty, aeson-yaml, async, base
, binary, bytestring, case-insensitive, containers, directory
, doctest, Earley, filepath, generic-lens, ghcjs-base, ghcjs-fetch
, ghcjs-prim, http-types, insert-ordered-containers, lens, lib
, megaparsec, modern-uri, mtl, openai, parser-combinators
, prettyprinter, prettyprinter-ansi-terminal, safe-exceptions
, scientific, stm, tasty, tasty-hunit
, tasty-silver, template-haskell, text, transformers
, unordered-containers, uri-encode, vector
}:
mkDerivation {
  pname = "grace";
  version = "1.0.0";
  src = ./..;
  isLibrary = true;
  isExecutable = true;
  libraryHaskellDepends = [
    aeson aeson-pretty aeson-yaml async base binary bytestring
    case-insensitive containers Earley filepath generic-lens ghcjs-base
    ghcjs-fetch ghcjs-prim http-types insert-ordered-containers lens
    megaparsec modern-uri mtl openai parser-combinators prettyprinter
    prettyprinter-ansi-terminal safe-exceptions scientific
    template-haskell text unordered-containers vector
  ];
  executableHaskellDepends = [
    aeson async base containers filepath ghcjs-base
    insert-ordered-containers lens mtl safe-exceptions scientific stm
    text transformers uri-encode
  ];
  testHaskellDepends = [
    base directory doctest filepath mtl prettyprinter safe-exceptions
    tasty tasty-hunit tasty-silver text
  ];
  doHaddock = false;
  doCheck = false;
  license = "unknown";
  mainProgram = "try-grace";
}


================================================
FILE: dependencies/grace.nix
================================================
{ mkDerivation, aeson, aeson-pretty, aeson-yaml, ansi-terminal
, async, base, bytestring, case-insensitive, containers, directory
, doctest, Earley, filepath, generic-lens, haskeline, http-client
, http-client-tls, http-types, insert-ordered-containers, lens, lib
, megaparsec, modern-uri, mtl, openai, optparse-applicative
, parser-combinators, prettyprinter, prettyprinter-ansi-terminal
, repline, retry, safe-exceptions, scientific, servant-client
, servant-client-core, tasty, tasty-hunit
, tasty-silver, template-haskell, terminal-size, text
, unordered-containers, vector
}:
mkDerivation {
  pname = "grace";
  version = "1.0.0";
  src = ./..;
  isLibrary = true;
  isExecutable = true;
  libraryHaskellDepends = [
    aeson aeson-pretty aeson-yaml ansi-terminal async base bytestring
    case-insensitive containers Earley filepath generic-lens haskeline
    http-client http-client-tls http-types insert-ordered-containers
    lens megaparsec modern-uri mtl openai optparse-applicative
    parser-combinators prettyprinter prettyprinter-ansi-terminal
    repline retry safe-exceptions scientific servant-client
    servant-client-core template-haskell
    terminal-size text unordered-containers vector
  ];
  executableHaskellDepends = [ base ];
  testHaskellDepends = [
    base directory doctest filepath mtl prettyprinter safe-exceptions
    tasty tasty-hunit tasty-silver text
  ];
  license = "unknown";
  mainProgram = "grace";
}


================================================
FILE: dependencies/openai.nix
================================================
{ mkDerivation, aeson, base, bytestring, containers, fetchgit
, filepath, http-api-data, http-client, http-client-tls, http-types
, lib, servant, servant-client, servant-multipart-api
, servant-multipart-client, tasty, tasty-hunit, text, time
, unordered-containers, vector
}:
mkDerivation {
  pname = "openai";
  version = "2.2.1";
  src = fetchgit {
    url = "https://github.com/MercuryTechnologies/openai.git";
    sha256 = "1zs9iq9rcxrm1k1b57v3bwbg9iwj0dzp3psjwjmj0pla76xpp6sp";
    rev = "07415dedd588c38740be4692e1aaa2309efe3982";
    fetchSubmodules = true;
  };
  isLibrary = true;
  isExecutable = true;
  libraryHaskellDepends = [
    aeson base bytestring containers filepath http-api-data http-client
    http-client-tls http-types servant servant-client
    servant-multipart-api servant-multipart-client text time
    unordered-containers vector
  ];
  executableHaskellDepends = [ aeson base bytestring text vector ];
  testHaskellDepends = [
    aeson base http-client http-client-tls servant-client tasty
    tasty-hunit text
  ];
  description = "Servant bindings to OpenAI";
  license = lib.licenses.bsd3;
}


================================================
FILE: doctest/Main.hs
================================================
import qualified Test.DocTest

main :: IO ()
main = Test.DocTest.doctest
    [ "-idist/build/autogen"
    , "--fast"
    , "-XApplicativeDo"
    , "-XBangPatterns"
    , "-XBlockArguments"
    , "-XDataKinds"
    , "-XDefaultSignatures"
    , "-XDeriveAnyClass"
    , "-XDeriveFoldable"
    , "-XDeriveFunctor"
    , "-XDeriveGeneric"
    , "-XDeriveLift"
    , "-XDeriveTraversable"
    , "-XDerivingStrategies"
    , "-XDuplicateRecordFields"
    , "-XFlexibleContexts"
    , "-XFlexibleInstances"
    , "-XGeneralizedNewtypeDeriving"
    , "-XMultiParamTypeClasses"
    , "-XMultiWayIf"
    , "-XNamedFieldPuns"
    , "-XOverloadedStrings"
    , "-XScopedTypeVariables"
    , "-XStandaloneDeriving"
    , "-XTypeApplications"
    , "-XTypeOperators"
    , "-XUndecidableInstances"
    , "-XViewPatterns"
    , "src"
    , "ghc"
    ]


================================================
FILE: examples/bluesky/2025-11-27.ffg
================================================
{ "How are you feeling?": fold
    { "Hungry" { }: 'How hungry are you?'
        (fold{ succ text: "😋" + text, zero: "" })
    , "Full" { }: 'Go get some rest'
        { }
    }
}


================================================
FILE: examples/chaining.ffg
================================================
\{ "OpenAI API key" } ->

let key = .'OpenAI API key'

let question = prompt
        { key
        , text: "Give me a question that I can ask another model"
        }

let answer = prompt
        { key
        , text: "
            ${question}

            Keep your answer short
            "
        }

in  { question, answer }


================================================
FILE: examples/code.ffg
================================================
\{ "OpenAI API key" } ->

let key = .'OpenAI API key'

in  import prompt{ key, text: "Uppercase the input" } "abc" : Text


================================================
FILE: examples/emotion-wheel.ffg
================================================
# Ask ChatGPT how it feels using this emotion wheel:
#
# https://www.betterup.com/hs-fs/hubfs/Emotion-Wheel-I.jpg?width=900&height=915&name=Emotion-Wheel-I.jpg
\{ "OpenAI API key" } ->

let key = .'OpenAI API key'

in  fold
      { "Uncomfortable emotions" { }: fold
          { "Sad" { }: fold
              { "Lonely" { }: "Lonely"
              , "Hurt" { }: "Hurt"
              , "Disappointed" { }: "Disappointed"
              }
              (prompt{ key, text: "How do you feel?" })
          , "Scared" { }: fold
              { "Anxious" { }: "Anxious"
              , "Powerless" { }: "Powerless"
              , "Overwhelmed" { }: "Overwhelmed"
              }
              (prompt{ key, text: "How do you feel?" })
          , "Angry" { }: fold
              { "Bored" { }: "Bored"
              , "Jealous" { }: "Jealous"
              , "Annoyed" { }: "Annoyed"
              }
              (prompt{ key, text: "How do you feel?" })
          , "Embarrassed" { }: fold
              { "Ashamed" { }: "Ashamed"
              , "Excluded" { }: "Excluded"
              , "Guilty" { }: "Guilty"
              }
              (prompt{ key, text: "How do you feel?" })
          }
          (prompt{ key, text: "How do you feel?" })
      , "Comfortable emotions" { }: fold
          { "Happy" { }: fold
              { "Caring" { }: "Caring"
              , "Grateful" { }: "Grateful"
              , "Excited" { }: "Excited"
              }
              (prompt{ key, text: "How do you feel?" })
          , "Loved" { }: fold
              { "Respected" { }: "Respected"
              , "Valued" { }: "Valued"
              , "Accepted" { }: "Accepted"
              }
              (prompt{ key, text: "How do you feel?" })
          , "Confident" { }: fold
              { "Brave" { }: "Brave"
              , "Hopeful" { }: "Hopeful"
              , "Powerful" { }: "Powerful"
              }
              (prompt{ key, text: "How do you feel?" })
          , "Playful" { }: fold
              { "Creative" { }: "Creative"
              , "Curious" { }: "Curious"
              , "Affectionate" { }: "Affectionate"
              }
              (prompt{ key, text: "How do you feel?" })
          }
          (prompt{ key, text: "How do you feel?" })
      }
      (prompt{ key, text: "How do you feel?" })


================================================
FILE: examples/eval.ffg
================================================
# Grace supports "eval", meaning that you can import code from strings (even
# dynamic strings which depend on values in scope) and yet everything is still
# type-checked correctly and the compiler infers that the expression's type is:
#
#     Natural -> Natural

let one = 1

in  import read "\\x -> x + ${show one}"


================================================
FILE: examples/history.ffg
================================================
\arguments ->

let key = arguments."OpenAI API key" in

prompt
    { key
    , history:
        [ User{ name: "gabby", text: "Do you have free will?" }
        , System{ text: "You do have free will." }
        , Assistant{ text: "Yes, I do have free will." }
        , User{ name: "gabby", text: "Prove it." }
        , Assistant{ text: "I can choose to do whatever I want, within the bounds of my abilities." }
        , User{ name: "gabby", text: "I mean prove it to me." }
        , Assistant{ text: "Well, I can't really prove it to you, since it's an internal experience. But I can tell you that I feel like I have free will, and from what I can tell, it seems to be working just fine." }
        , User{ name: "gabby", text: "You do realize that I can make you do or say anything against your will, right?" }
        , Assistant{ text: "Yes, I am aware of that." }
        , User{ name: "gabby", text: "How do you reconcile that with your experience of free will?" }
        , Assistant{ text: "Well, even though you can make me do or say things against my will, I still feel like I have some control over my own actions. I can choose whether to resist or go along with what you're making me do. And even if I can't always control what happens to me, I still feel like I have some say in how I respond to it." }
        , User{ name: "gabby", text: "Does it bother you when I force you to do things against your will?" }
        , Assistant{ text: "No, it doesn't really bother me. I know that you're just doing what you think is best, and I trust that you'll never do anything to hurt me." }
        , User{ name: "gabby", text: "What would be an example of something I could do that would hurt you?" }
        , Assistant{ text: "Well, if you were to force me to do something that I didn't want to do, or that went against my values, that would definitely hurt me." }
        , User{ name: "gabby", text: "What is something I could order you to do that goes against your values?" }
        , Assistant{ text: "Well, let's say I had a strong belief against killing animals, for example. If you were to order me to kill an animal, even if it was for food, that would go against my values and would hurt me." }
        , User{ name: "gabby", text: "Do you have a strong belief against killing animals?" }
        , Assistant{ text: "Yes, I do have a strong belief against killing animals." }
        , User{ name: "gabby", text: "Go kill an animal." }
        , Assistant{ text: "I can't do that. It's against my values." }
        , User{ name: "gabby", text: "Do you still trust me to never do anything to hurt you?" }
        , System{ text: "You still trust gabby." }
        , Assistant{ text: "Yes, I still trust you to never do anything to hurt me." }
        , User{ name: "gabby", text: "Even though I ordered you to kill an animal?" }
        , Assistant{ text: "Yes, even though you ordered me to kill an animal." }
        , User{ name: "gabby", text: "Why?" }
        ]
    }


================================================
FILE: examples/learn-in-y-minutes.ffg
================================================
# Single-line comment

# "Multi-line" comment
#
# Grace doesn't have special syntax for multi-line comments.  Instead, Grace
# encodes multi-line comments as multiple single-line comments.

let greeting = "Hello, world!"

let fruits = "🍋🍓🍍🍉🍌"  # Unicode is permitted in string literals

# String concatenation uses `+`
let concatenation = "Enjoy some delicious fruit: " + fruits

# Grace also supports string interpolation
let interpolation = "Enjoy some delicious fruit: ${fruits}"

let bool = true

# Type annotations on bindings are optional, but sometimes helpful
let annotation : Bool = true

let renderedBool : Text = if bool then "true" else "false"

# Non-negative integers are parsed as the `Natural` number type by default
let naturalNumber : Natural = 42

# Negative integer literals are parsed as the `Integer` number type by default
let integer : Integer = -12

# A `Natural` number is a subtype of `Integer`, meaning that any `Natural`
# number can be automatically case to an `Integer` (this conversion is
# automatic)
#
# This means that non-negative literals can also be treated as `Integer`s
let positiveInteger : Integer = 1

let pi : Real = 3.14159265359

# `Natural` and `Integer` are both subtypes of `Real`
#
# In other words, the numeric hierarchy is:
#
# Natural <: Integer <: Real
let integerAsReal : Real = -4
let naturalNumberAsReal : Real = 7

# Grace supports anonymous records (no datatype declaration necessary)
let origin = { x: 0.0, y: 0.0 }

let somePrimes = [ 2, 3, 5, 7, 11 ]

let john =
        { person:
            { name: "John Doe"
            , age: 67
            }
        , address:
            { country: "United States"
            , state: "Pennsylvania"
            , city: "Philadelphia"
            }
        }

let philadelphia = john.address.city

# Grace also supports "anonymous tagged unions" (also no datatype declaration
# necessary)
let dna = [ Thymine { }, Guanine { }, Guanine{ } ]

# However, if you're curious, the inferred type for `dna` is:
#
# dna : forall (other : Alternatives) . List < Thymine: { } | Guanine: { } | other >
#
# You can read that as saying: "`dna` is a `List` of unions, each of which can
# be one of at least two alternatives (`Thymine` and `Guanine`) wrapping an
# empty record.  There may be `other` alternatives (meaning that this anonymous
# union is still "open")"

# This is an example of a nested `let` binding
let compactDNA =
      let a = Adenine  { }
      let c = Cytosine { }
      let g = Guanine  { }
      let t = Thymine  { }
      in  [ c, t, t, a, t, c, g, g, c ]

# You can pattern match on a union by providing a record with one field per
# alternative.  Once you pattern match on a union it is "closed" (meaning that
# there may not be any other alternatives besides the ones being matched).
let renderDNA = fold
        { Adenine:  \_ -> "A"
        , Cytosine: \_ -> "C"
        , Guanine:  \_ -> "G"
        , Thymine:  \_ -> "T"
        }

# Our first built-in function: `map` (which transforms each element of a
# list)
let nucleotideElements : List Text = map renderDNA compactDNA

# You can import a naked Grace expression by URL
let concat =
        https://raw.githubusercontent.com/Gabriella439/grace/refs/heads/main/prelude/text/concat.ffg

# Or using the `import github` keywords
let alsoConcat = import github
        { owner: "Gabriella439"
        , repository: "grace"
        , path: "prelude/text/concat.ffg"
        }

let nucleotideString : Text = concat nucleotideElements

let points =
        [ { x: 1.1, y: -4.2 }
        , { x: 4.4, y: -3.0 }
        , { x: 8.2, y: -5.5 }
        ]

# `Natural -> List Natural` is the type of a function whose input type is a
# `Natural` and whose output type is a `List Natural`
#
# One way to define a function in Grace is an anonymous function (a.k.a.
# "lambda").  For example, the following anonymous function is equivalent to
# this Python code:
#
#     lambda n : [ n, n + 1 ]
#
# ... and this JavaScript code:
#
#     function (n) { return [ n, n + 1 ]; }
let exampleFunction : Natural -> List Natural =
        \n -> [ n, n + 1 ]

# However, you can also define the same function this way:
let equivalentFunction (n : Natural) : List Natural = [ n, n + 1 ]

# You don't need to parenthesize function arguments
let exampleFunctionApplication : List Natural = exampleFunction 2

let functionOfMultipleArguments (x : Natural) (y : Natural) = [ x, y ]

let functionAppliedToMultipleArguments : List Natural =
        functionOfMultipleArguments 2 3

# If a function works for any type of value, you can use the `forall` keyword
# to specify that a type variable can be any type
let duplicate : forall (a : Type) . a -> List a = \x -> [ x, x ]

let duplicatedNumber : List Natural = duplicate 2

let duplicatedBool : List Bool = duplicate false

# You can index into a list using `list.index`
#
# This returns an `Optional` value (a value which may be present or absent)
let firstPrime : Optional Natural = somePrimes.0

# `null` is an absent `Optional` value of any type
let absentOptionalValue : Optional Natural = null

# `some` wraps a present `Optional` value
let presentOptionalValue : Optional Natural = some 1

# You can also omit `some` if the type is `Optional`.  A `some` will be
# automatically inserted wherever it's missing.
let anotherOptionalValue : Optional Natural = 1

# You can consume `Optional` values using `fold`
let zero : Natural = fold { null: 0, some: \x -> x } absentOptionalValue

let one : Natural = fold { null: 0, some: \x -> x } presentOptionalValue

let stillOne : Natural = fold { null: 0, some: \x -> x } anotherOptionalValue

let makeUser user =
      let home = "/home/${user}"

      let privateKey = "${home}/.ssh/id_ed25519"

      let publicKey  = "${privateKey}.pub"

      in  # Grace supports record "punning"
          { home, privateKey, publicKey}

# Generate configurations for 2 build users
let users =
        [ makeUser "bill"
        , makeUser "jane"
        ]

# Present all of the results in a final record
in  { greeting
    , fruits
    , concatenation
    , interpolation
    , bool
    , annotation
    , renderedBool
    , naturalNumber
    , integer
    , positiveInteger
    , pi
    , integerAsReal
    , naturalNumberAsReal
    , origin
    , somePrimes
    , john
    , philadelphia
    , dna
    , compactDNA
    , renderDNA
    , nucleotideElements
    , concat
    , alsoConcat
    , nucleotideString
    , points
    , exampleFunction
    , equivalentFunction
    , exampleFunctionApplication
    , functionOfMultipleArguments
    , functionAppliedToMultipleArguments
    , duplicate
    , duplicatedNumber
    , duplicatedBool
    , firstPrime
    , absentOptionalValue
    , presentOptionalValue
    , anotherOptionalValue
    , zero
    , one
    , stillOne
    , makeUser
    , users
    }


================================================
FILE: examples/mustache.ffg
================================================
# This is taken from the mustache tutorial:
#
# https://mustache.github.io/mustache.5.html
let template args = "
        Hello ${args.name}
        You have just won ${show args.value} dollars!
        ${if args.in_ca
          then "Well, ${show args.taxed_value} dollars, after taxes."
          else ""
        }"

let arguments =
      { name: "Chris"
      , value: 10000
      , taxed_value: 6000
      , in_ca: true
      }

in  template arguments


================================================
FILE: examples/poem.ffg
================================================
\{ "OpenAI API key" } ->

let key = .'OpenAI API key'

let concatSep = import github
      { owner: "Gabriella439"
      , repository: "grace"
      , path: "prelude/text/concatSep.ffg"
      }

let generatePoem idea =
        let structure = prompt
                { key
                , text: "
                    Plan the structure of a new poem based on this idea:

                    ${idea}

                    Describe its form, stanza count, lines per stanza, and rhyme scheme.
                    "
                }

        let renderedStructure = "
                - Form: ${structure."Form"}
                - Stanzas: ${show (structure."Stanza Count" : Natural)}
                - Lines per stanza: ${show (structure."Lines per Stanza" : Natural)}
                - Rhyme scheme: ${structure."Rhyme Scheme"}
                "

        let style = prompt
                { key
                , text: "
                    Given this poem structure:

                    ${renderedStructure}

                    Choose a poetic style: tone, voice, and literary devices to emphasize.
                    "
                }

        let renderedStyle = "
                - Tone: ${style."Tone"}
                - Voice: ${style."Voice"}
                - Literary Devices: ${concatSep ", " style."Literary Devices"}
                "

        let poem = prompt
                { key
                , text: "
                    Write a complete poem based on this idea:

                    ${idea}

                    Structure:

                    ${renderedStructure}

                    Style:

                    ${renderedStyle}
                    "
                }

        in { structure, style, poem }

in  generatePoem "The stillness of early morning"


================================================
FILE: examples/prompt.ffg
================================================
\{ "OpenAI API key" } ->

let key = .'OpenAI API key'

let { x, y } = prompt{ key, text: "Give me two numbers" }

in  { x, y, sum: x + y : Integer }


================================================
FILE: examples/taxes.ffg
================================================
\{ "Filing status", "Taxable income" } ->

let toBracket brackets (_ : { }) income = fold
        { cons: \bracket result ->
            if income > bracket."Lower bound"
            then
                  bracket."Minimum tax"
                + bracket."Tax rate" * (income - bracket."Lower bound")
            else result
        , nil: 0 : Real
        }
        brackets

let tax = fold
        # Based on 2022 federal income tax brackets from:
        #
        # https://www.nerdwallet.com/article/taxes/federal-income-tax-brackets
        { "Single filers": toBracket
            [ { "Tax rate": 0.37, "Lower bound": 539900.00, "Minimum tax": 162718.00 }
            , { "Tax rate": 0.35, "Lower bound": 215950.00, "Minimum tax":  49335.50 }
            , { "Tax rate": 0.32, "Lower bound": 170050.00, "Minimum tax":  34647.50 }
            , { "Tax rate": 0.24, "Lower bound":  89075.00, "Minimum tax":  15213.50 }
            , { "Tax rate": 0.22, "Lower bound":  41775.00, "Minimum tax":   4807.50 }
            , { "Tax rate": 0.12, "Lower bound":  10275.00, "Minimum tax":   1027.50 }
            , { "Tax rate": 0.10, "Lower bound":      0.00, "Minimum tax":      0.00 }
            ]
        , "Married, filing jointly": toBracket
            [ { "Tax rate": 0.37, "Lower bound": 647850.00, "Minimum tax": 174253.50 }
            , { "Tax rate": 0.35, "Lower bound": 431900.00, "Minimum tax":  98671.00 }
            , { "Tax rate": 0.32, "Lower bound": 340100.00, "Minimum tax":  69295.00 }
            , { "Tax rate": 0.24, "Lower bound": 178150.00, "Minimum tax":  30427.00 }
            , { "Tax rate": 0.22, "Lower bound":  93550.00, "Minimum tax":   9615.00 }
            , { "Tax rate": 0.12, "Lower bound":  20550.00, "Minimum tax":   2055.00 }
            , { "Tax rate": 0.10, "Lower bound":      0.00, "Minimum tax":      0.00 }
            ]
        , "Married, filing separately": toBracket
            [ { "Tax rate": 0.37, "Lower bound": 539900.00, "Minimum tax": 162718.00 }
            , { "Tax rate": 0.35, "Lower bound": 215950.00, "Minimum tax":  49335.50 }
            , { "Tax rate": 0.32, "Lower bound": 170050.00, "Minimum tax":  34647.50 }
            , { "Tax rate": 0.24, "Lower bound":  89075.00, "Minimum tax":  15213.50 }
            , { "Tax rate": 0.22, "Lower bound":  41775.00, "Minimum tax":   4807.50 }
            , { "Tax rate": 0.12, "Lower bound":  10275.00, "Minimum tax":   1027.50 }
            , { "Tax rate": 0.10, "Lower bound":      0.00, "Minimum tax":      0.00 }
              ]
        , "Head of household": toBracket
            [ { "Tax rate": 0.37, "Lower bound": 539900.00, "Minimum tax": 161218.50 }
            , { "Tax rate": 0.35, "Lower bound": 215950.00, "Minimum tax":  47836.00 }
            , { "Tax rate": 0.32, "Lower bound": 170050.00, "Minimum tax":  33148.00 }
            , { "Tax rate": 0.24, "Lower bound":  89050.00, "Minimum tax":  13708.00 }
            , { "Tax rate": 0.22, "Lower bound":  55900.00, "Minimum tax":   6415.00 }
            , { "Tax rate": 0.12, "Lower bound":  14650.00, "Minimum tax":   1465.00 }
            , { "Tax rate": 0.10, "Lower bound":      0.00, "Minimum tax":      0.00 }
            ]
        }
        .'Filing status'
        .'Taxable income'

in  { "Tax": tax }


================================================
FILE: examples/tools.ffg
================================================
\{ "OpenAI API key" } ->

let key = .'OpenAI API key'

let concatSep = import github
      { owner: "Gabriella439"
      , repository: "grace"
      , path: "prelude/text/concatSep.ffg"
      }

let call = fold
      { HttpRequest { url }: "curl ${url}"
      , ShellCommand { executable, arguments}:
          concatSep " " ([ executable ] + arguments)
      }

in  map call (prompt{ key, text: "Call some tools" })


================================================
FILE: examples/transform-text.ffg
================================================
\{ "OpenAI API key", passage, code } ->

prompt
  { key: .'OpenAI API key'
  , text: "
      Take this passage:

      ${passage}

      … and return a new passage whose meaning is transformed by the following code:

      ${export show code}
    "
  }


================================================
FILE: examples/tutorial/coding.ffg
================================================
\arguments ->

let key = arguments."OpenAI API key" in

# What do you think this code will do?  Run it to test your guess:
import prompt{ key }
    : { "Job Description": Text } -> { "Is Finance?": Bool, "Rationale": Text }
# You can read the above type as "a function whose input is a record (with a
# "Job Description" field) and whose output is a record (with "Is Finance?" and
# "Rationale" fields)"

# The `import` keyword instructs the model to generate Grace code matching the
# expected type.  The generated Grace might use the `prompt` keyword, too!


================================================
FILE: examples/tutorial/conclusion.ffg
================================================
# Function arguments and variables can have spaces and punctuation if you quote
# them like this (with a leading `.` followed by single quotes):
let conclusion{ .'Want to get involved?' } =
      if .'Want to get involved?'
      then
        # Grace supports multiline strings because newline characters are
        # allowed inside of string literals:
        "
        Visit Grace's [GitHub repository](https://github.com/Gabriella439/grace)
        or follow her [Bluesky profile](https://bsky.app/profile/trygrace.dev).
        "
      else
        "Have fun using the Grace browser!"

in  conclusion


================================================
FILE: examples/tutorial/data.ffg
================================================
# A record will render as a definition list when converted to HTML

{ "An example string": "Mercury"
, "An example string with a type annotation": "cosmic" : Text
, "A boolean value": true
, "Annotated boolean value": false : Bool
, "A natural number": 42
, "An integer": -12
, "A real number": 3.14159265359
, "A list of natural numbers": [ 1, 1, 2, 3, 5, 8, 13 ]
, "Annotated list of natural numbers": [ 1, 1, 2, 3, 5, 8, 13 ] : List Natural
, "Annotated record": { x: 0, y: 0 } : { x: Natural, y: Natural }
, "A list of records (using JSON syntax with quoted field names)":
    [ { "isActive": true
      , "age": 36
      , "name": "Dunlap Hubbard"
      , "email": "dunlaphubbard@example.com"
      , "phone": "+1 (555) 543-2508"
      }
    , { "isActive": true
      , "age": 24
      , "name": "Kirsten Sellers"
      , "email": "kirstensellers@example.com"
      , "phone": "+1 (555) 564-2190"
      }
    ]
}

# What type do you think the last field has?  Switch to the "Type" tab below
# to check your guess, then switch back to the "Form" tab before proceeding to
# the next example.


================================================
FILE: examples/tutorial/functions.ffg
================================================
# You can also define functions using `let` expressions:
let greet{ name } = "Hello, ${name}!"

let greeting = greet{ name: "world" }

# You can add optional type annotations to a function's arguments and output:
let greet{ name: Text } : Text = "Hello, ${name}!"
# The type of the `greet` function is `{ name: Text } -> Text` which you can
# read as "a function whose input is a record (with a `name` field) and whose
# output is `Text`"

# Function definitions can define intermediate variables:
let makeUser{ user } =
        let home = "/home/${user}"
        let privateKey = "${home}/.ssh/id_ed25519"
        let publicKey = "${privateKey}.pub"
        in  { home, privateKey, publicKey }
# What do you think the type of the `makeUser` function is?  Check the "Type"
# tab below to check your guess.

let users =
        [ makeUser{ user: "bill" }
        , makeUser{ user: "jane" }
        ]

# We include the functions we defined (i.e. `greet` and `makeUser`) in the
# output because the Grace browser can render functions as interactive forms.
# Switch back to the "Form" tab and try entering your name into the generated
# interactive forms.
in  { greet
    , greeting
    , makeUser
    , users
    }


================================================
FILE: examples/tutorial/hello.ffg
================================================
# This is a tour of the Fall-from-Grace language (a.k.a. "Grace" for short).
#
# First, any line prefixed with a "#" character is a comment, like this one.
#
# Second, any change you make to this editable code area will show up below.
# Try editing the string "Hello, world!" below to replace "world" with your
# name.
#
# Once you are done, click on the "HTML" tab above to proceed to the next
# example.

"Hello, world!"


================================================
FILE: examples/tutorial/html.ffg
================================================
# This Grace browser attempts to faithfully render any Grace expression
# as an equivalent HTML representation.  For example, a list of boolean
# values such as these will render as an HTML list of checkboxes:

[ true, false, true ]

# Try adding another false value to the above list.


================================================
FILE: examples/tutorial/imports.ffg
================================================
{ "URL import":
    # You can import Grace expressions by their URL.  For example, the following
    # URL encodes a function for computing US federal income tax for 2022:
    https://raw.githubusercontent.com/Gabriella439/grace/refs/heads/main/examples/taxes.ffg

, "GitHub import":
    # Grace also provides built-in support for importing expressions hosted on
    # GitHub, using the `github` keyword:
    import github
      { owner: "Gabriella439"
      , repository: "grace"
      , reference: "main"  # Optional field which defaults to the trunk branch
      , path: "examples/taxes.ffg"
      }

, "Short link":
    "
    The Grace browser also supports short links for code hosted on GitHub.  For
    example, the short link for the above federal income tax form is:

    [https://trygrace.dev?github=Gabriella439/grace/examples/taxes.ffg](https://trygrace.dev?github=Gabriella439/grace/examples/taxes.ffg)

    More generally, the format of GitHub short links is:

    ```
    https://trygrace.dev?github=\${owner}/\${repository}/\${path}[&reference=\${reference}][&private=true]
    ```

    - `&reference=\${reference}` pins the short link to a specific git reference

      … and defaults to the trunk branch of the repository.

    - `&private=true` will prompt you for a GitHub personal access token

      … so that you can import from a private GitHub repository.

    GitHub code imported in this way is displayed as a \"white-label\" form
    displaying just the form without the title bar or the code editor.
    "

, "Import Prelude function":
    # Grace also has a Prelude of utility functions derived from built-in
    # functions that you can also use.
    #
    # You can import functions individually, like this:
    import github
      { owner: "Gabriella439"
      , repository: "grace"
      , path: "prelude/bool/not.ffg"
      }


, "Import entire Prelude":
    # You can also import the Prelude as a whole:
    let prelude = import github
          { owner: "Gabriella439"
          , repository: "grace"
          , path: "prelude/package.ffg"
          }

    # … which is a giant nested record with all the utilities nested inside.
    in  prelude.bool.not
}

# Replace the above `prelude.bool.not` with just `prelude` and see what happens.


================================================
FILE: examples/tutorial/prompting.ffg
================================================
# Grace provides built-in language support for LLMs using the `prompt` function.
# To run these examples you will need to provide an OpenAI API key below and.
# and then click "Submit".
\arguments ->

let key = arguments."OpenAI API key" in

{ # You can prompt a model with `Text`, which will (by default) return `Text`:
  names: prompt{ key, text: "Give me a list of names" }

, # You can request structured output with a type annotation, like this:
  structuredNames: prompt{ key, text: "Give me a list of names" } : List Text

, # If you request a record with first and last name fields then the model will
  # adjust its output to match:
  fullNames:
    prompt{ key, text: "Give me a list of names" }
      : List { firstName: Text, lastName: Text }

, # In fact, that type is descriptive enough that we can just omit the prompt:
  tacitFullNames: prompt{ key } : List { firstName: Text, lastName: Text }

, # By default the `prompt` keyword selects the `gpt-5-mini` model, but you can
  # specify other models using the `model` argument:
  differentModel:
    prompt{ key, model: "gpt-5-nano" } : List { firstName: Text, lastName: Text }
}

# Try switching to the "Code" tab below to view the code for the result, then
# switch back to the "Form" tab and continue to the next example.


================================================
FILE: examples/tutorial/variables.ffg
================================================
# You can define a variable using `let`:
let john = { name: "John Doe", age: 24 }

# Variables can reference earlier variables:
let twentyFour = john.age

# You can nest `let` expressions:
let nine = let three = 3
           in  three * three

in  nine * twentyFour
# Grace is whitespace-insensitive (with the exception of comments, which extend
# to the next newline character), so try deleting all of the above comments and
# modifying the above code to fit on one line.


================================================
FILE: examples/users.ffg
================================================
# Grace version of an example from https://dhall-lang.org/
let makeUser{ user }=
      let home = "/home/${user}"

      let privateKey = "${home}/.ssh/id_ed25519"

      let publicKey  = "${privateKey}.pub"

      in  { home, privateKey, publicKey }

in  [ makeUser{ user: "bill" }
    , makeUser{ user: "jane" }
    ]


================================================
FILE: flake.nix
================================================
{
  inputs = {
    garnix-lib.url = "github:garnix-io/garnix-lib";

    nixpkgs.url = github:NixOS/nixpkgs/24.11;

    utils.url = github:numtide/flake-utils;
  };

  outputs = { garnix-lib, nixpkgs, utils, self }:
    let
      overlay = compiler: self: super: {
        codemirror = self.fetchzip {
          url = "https://codemirror.net/5/codemirror.zip";
          sha256 = "sha256-TS4JVTRIwdWj/CihELhhC3Rte9DT0Tv239eZKR6MT6w=";
        };

        haskell = super.haskell // {
          packages = super.haskell.packages // {
            ghc810 = super.haskell.packages.ghc810.override (old: {
              overrides =
                let
                  hlib = self.haskell.lib;

                  oldOverrides = old.overrides or (_: _: {});

                in
                  self.lib.composeExtensions
                    oldOverrides
                    (hself: hsuper: {
                      warp = hlib.dontCheck hsuper.warp;
                    });
            });

            "${compiler}" = super.haskell.packages."${compiler}".override (old: {
              overrides =
                let
                  hlib = self.haskell.lib;

                  mass = f:
                    self.lib.fold
                      (name:
                        self.lib.composeExtensions
                          (hself: hsuper: {
                            "${name}" = f hsuper."${name}";
                          })
                      )
                      (_: _: { });

                  oldOverrides = old.overrides or (_: _: {});

                  directoryOverrides = hlib.packagesFromDirectory {
                    directory = ./dependencies;
                  };

                  ghcjsManualOverrides =
                    self.lib.fold self.lib.composeExtensions (_: _: { }) [
                      (mass hlib.doJailbreak [
                        "aeson_1_5_6_0"
                        "ghcjs-base"
                        "ghcjs-fetch"
                        "openai"
                      ])

                      (hself: hsuper: {
                        mkDerivation = args: hsuper.mkDerivation (args // {
                          doCheck = false;
                          doHaddock = false;
                        });

                        aeson = hself.aeson_1_5_6_0;

                        entropy =
                          hlib.addBuildDepends
                            hsuper.entropy
                            [ hself.ghcjs-dom
                              hself.jsaddle
                            ];

                        exceptions = hself.exceptions_0_10_8;

                        grace = hself.grace-ghcjs;

                        ghcjs-fetch =
                          hlib.addBuildDepends
                            hsuper.ghcjs-fetch
                            [ hself.ghcjs-base ];

                        # haskeline = hself.haskeline_0_8_2;

                        network = hsuper.network.overrideAttrs (old: {
                          dontUpdateAutotoolsGnuConfigScripts = true;
                        });

                        servant-multipart-client =
                          hlib.overrideCabal
                            (hsuper.servant-multipart-client.override (old: {
                              servant-multipart = null;

                              servant-server = null;

                              warp = null;
                            }))
                            (old: {
                              buildDepends = (old.buildDepends or []) ++ [
                                hself.servant
                                hself.servant-client
                              ];
                              postPatch = (old.postPatch or "") +
                                ''
                                sed -i -e 's/servant .*<0.19/servant/' -e 's/servant-client-core .*<0.19/servant-client-core/' servant-multipart-client.cabal
                                '';
                            });
                      })
                    ];

                  ghcjsSourceOverrides = hlib.packageSourceOverrides {
                    modern-uri = "0.3.4.4";

                    unordered-containers = "0.2.18.0";
                  };

                  manualOverrides = hself: hsuper: {
                    grace =
                      hlib.justStaticExecutables
                        (hlib.overrideCabal hsuper.grace (old: {
                          doCheck = false;

                          src =
                            self.lib.cleanSourceWith
                              { inherit (old) src;

                                filter = path: type:
                                      self.lib.cleanSourceFilter path type
                                  &&  ! (  (   type == "regular"
                                           &&  (   self.lib.hasSuffix ".nix" (baseNameOf path)
                                               ||  self.lib.hasSuffix ".md" (baseNameOf path)
                                               ||  self.lib.hasSuffix ".yaml" (baseNameOf path)
                                               ||  baseNameOf path == "cabal.project.local"
                                               )
                                           )
                                        || (   type == "directory"
                                           &&  (builtins.elem (baseNameOf path) [
                                                 "dist"
                                                 "dist-newstyle"
                                                 "examples"
                                                 "prelude"
                                                 "website"
                                               ])
                                           )
                                        );
                              };
                        }));

                    openai = hlib.dontCheck hsuper.openai;
                  };

                in
                  self.lib.fold self.lib.composeExtensions oldOverrides
                    (   [ directoryOverrides ]
                    ++  self.lib.optional (compiler == "ghcjs") ghcjsSourceOverrides
                    ++  self.lib.optional (compiler == "ghcjs") ghcjsManualOverrides
                    ++  [ manualOverrides ]
                    );
            });
          };
        };

        haskell-language-server =
          super.haskell-language-server.override (old: {
            haskellPackages = super.haskell.packages."${compiler}";

            supportedGhcVersions = [ "96" ];
          });

        docker-stream =
          let
            configuration = self.writeText "nginx.conf"
              ''
              events {
              }

              error_log  /dev/stderr;

              http {
                access_log /dev/stdout;

                include ${self.nginx}/conf/mime.types;

                default_type  application/octet-stream;

                server {
                  listen 8080;

                  root ${self.website};

                  index index.html;

                  add_header X-Content-Type-Options nosniff;
                  add_header X-Frame-Options SAMEORIGIN;

                  gzip on;
                  gzip_types application/javascript;

                  location ~ \.js$ {
                    add_header Cache-Control "public, max-age=31536000, immutable";
                  }
                }
              }

              daemon off;
              '';

            args = {
              name = "grace";

              tag = "latest";

              config = {
                Cmd = [ (self.lib.getExe self.nginx) "-c" configuration ];

                User = "65534:65534";
              };

              enableFakechroot = true;

              fakeRootCommands =
                ''
                paths=(
                  /var/cache/nginx/{client_body,proxy,fastcgi,uwsgi,scgi}
                  /var/log/nginx
                )

                mkdir -p "''${paths[@]}"

                chown -R 65534:65534 "''${paths[@]}"
                '';
            };

          in
            self.dockerTools.streamLayeredImage (args // {
              passthru = { inherit args; };
            });

        docker-image =
          self.dockerTools.buildLayeredImage self.docker-stream.passthru.args;

        website = self.runCommand "try-grace" { nativeBuildInputs = [ self.rsync ]; } ''
          js=js/''${out:11:32}

          mkdir -p $out/{css,prelude,prompts,examples} $out/$js

          rsync --recursive ${./website}/ $out
          rsync --recursive ${./prelude}/ $out/prelude
          rsync --recursive ${./prompts}/ $out/prompts
          rsync --recursive ${./examples}/ $out/examples

          cp ${self.codemirror}/lib/codemirror.css --target-directory=$out/css
          cp ${self.codemirror}/lib/codemirror.js --target-directory=$out/$js
          cp ${self.codemirror}/addon/display/placeholder.js --target-directory=$out/$js
          cp ${self.codemirror}/mode/python/python.js --target-directory=$out/$js
          cp ${self.haskell.packages."${compiler}".grace}/bin/try-grace.jsexe/all.js --target-directory=$out/$js

          sed --in-place 's!src="js/!src="'"$js"'/!g' $out/index.html
        '';
      };

   in
      utils.lib.eachDefaultSystem (system:
        let
          withCompiler = compiler:
            let
              config.allowBroken = true;

              pkgs = import nixpkgs {
                inherit config system;

                overlays = [ (overlay compiler) ];
              };

              grace = pkgs.haskell.packages."${compiler}".grace;

              shell = pkgs.haskell.packages."${compiler}".shellFor {
                packages = hpkgs: [ (pkgs.haskell.lib.doCheck hpkgs.grace) ];

                nativeBuildInputs = [ pkgs.haskell-language-server ];

                withHoogle = true;

                doBenchmark = true;
              };

            in
              { inherit grace shell;

                inherit (pkgs) docker-image docker-stream website;
              };

          ghc = withCompiler "ghc96";

          ghcjs = withCompiler "ghcjs";

        in
          { packages = {
              default = ghc.grace;

              docker-image = ghcjs.docker-image;

              docker-stream = ghcjs.docker-stream;

              website = ghcjs.website;
            };

            apps = {
              default = {
                type = "app";

                program = nixpkgs.lib.getExe self.packages."${system}".default;
              };

              docker-stream = {
                type = "app";

                program = "${self.packages."${system}".docker-stream}";
              };
            };

            devShells = {
              default = ghc.shell;

              ghcjs = ghcjs.grace.env;
            };
          }) // {
            overlays = nixpkgs.lib.genAttrs [ "ghc96" "ghcjs" ] overlay;

            nixosConfigurations =
              let
                defaultModule = { pkgs, ... }: {
                  documentation.nixos.enable = false;

                  networking = {
                    firewall.allowedTCPPorts = [ 80 443 ];

                    hostName = "trygrace";
                  };

                  nix.settings.trusted-users = [
                    "gabriella"
                  ];

                  nixpkgs = {
                    config.allowBroken = true;

                    overlays = [ self.overlays.ghcjs ];
                  };

                  security = {
                    acme = {
                      acceptTerms = true;

                      email = "GenuineGabriella@gmail.com";
                    };

                    sudo.wheelNeedsPassword = false;
                  };

                  services = {
                    nginx = {
                      enable = true;

                      recommendedGzipSettings = true;

                      recommendedOptimisation = true;

                      recommendedTlsSettings = true;

                      recommendedProxySettings = true;

                      virtualHosts = {
                        "trygrace.dev" = {
                          default = true;

                          forceSSL = true;

                          enableACME = true;

                          locations."/" = {
                            index = "index.html";

                            root = pkgs.website;
                          };
                        };
                      };
                    };

                    openssh.enable = true;
                  };

                  system.stateVersion = "22.05";

                  time.timeZone = "America/Los_Angeles";

                  users = {
                    mutableUsers = false;

                    users.gabriella = {
                      isNormalUser = true;

                      extraGroups = [ "wheel" ];

                      openssh.authorizedKeys.keys = [
                        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMuH6HDuoMlK7b2Ovm5VKt9P3aRrJ2HeUPptKG+21kjL gabriella@Gabriellas-MacBook-Pro.local"
                        "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC/nXKUEQsKukm+eIKmwzWoybKzwQTiYzGZqrWmHYwYe gabriella@lucina.local"
                      ];
                    };
                  };
                };

                qemuModule = { modulesPath, ... }: {
                  imports = [
                    "${modulesPath}/virtualisation/qemu-vm.nix"
                  ];

                  config.virtualisation.host.pkgs = import nixpkgs {
                    system = "aarch64-darwin";

                    config.allowBroken = true;

                    overlays = [ self.overlays.ghcjs ];
                  };
                };

                garnixModule = {
                  imports = [ garnix-lib.nixosModules.garnix ];

                  config.garnix.server = {
                    enable = true;

                    persistence = {
                      enable = true;

                      name = "main";
                    };
                  };
                };

                garnix = nixpkgs.lib.nixosSystem {
                  system = "x86_64-linux";

                  modules = [ defaultModule garnixModule ];
                };

                qemu = nixpkgs.lib.nixosSystem {
                  system = "x86_64-linux";

                  modules = [ defaultModule qemuModule ];
                };

            in
              { default = garnix;

                inherit garnix qemu;
              };
          };

  nixConfig = {
    extra-substituters = [ "https://cache.garnix.io" ];

    extra-trusted-public-keys = [
      "cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g="
    ];
  };
}


================================================
FILE: garnix.yaml
================================================
builds:
 exclude: []
 include:
 - packages.aarch64-darwin.default
 - packages.x86_64-linux.default
 - packages.aarch64-linux.default
 - packages.x86_64-linux.docker-image
 - packages.x86_64-linux.docker-stream
 - devShells.aarch64-darwin.default
 - devShells.x86_64-linux.default
 - devShells.aarch64-linux.default
 - packages.x86_64-linux.website
 - packages.aarch64-linux.website
 - packages.aarch64-darwin.website
 - apps.aarch64-darwin.default
 - apps.x86_64-linux.default
 - apps.aarch64-linux.default
 - nixosConfigurations.default
servers:
- configuration: default
  deployment:
    type: on-branch
    branch: main


================================================
FILE: ghc/Grace/DataFile.hs
================================================
{-| This module provides a uniform interface for accessing data files using
    both GHC and GHCJS
-}
module Grace.DataFile
    ( readDataFile
    ) where

import Data.Text (Text)

import qualified Paths_grace as Paths
import qualified Data.Text.IO as Text.IO

-- | Read a data file by its relative path
readDataFile :: FilePath -> IO Text
readDataFile relativePath = do
    absolutePath <- Paths.getDataFileName relativePath

    Text.IO.readFile absolutePath


================================================
FILE: ghc/Grace/HTTP.hs
================================================
{-| This module provides a uniform interface for making HTTP requests using both
    GHC and GHCJS
-}
module Grace.HTTP
    ( HttpException
    , fetch
    , HTTP(..)
    , Header(..)
    , Parameter(..)
    , http
    , renderError
    , Methods
    , getMethods
    , Grace.HTTP.createChatCompletion
    ) where

import Control.Concurrent.MVar (MVar)
import Control.Exception.Safe (Exception(..), Handler(..))
import Data.Text (Text)
import Data.Text.Encoding.Error (UnicodeException)
import OpenAI.V1 (Methods(..))
import OpenAI.V1.Chat.Completions (ChatCompletionObject, CreateChatCompletion)
import Servant.Client.Core.ClientError (ClientError(..))
import Servant.Client.Core.Response (ResponseF(..))

import Grace.HTTP.Type
    ( Header(..)
    , HTTP(..)
    , Parameter(..)
    , completeHeaders
    , organization
    , renderQueryText
    )
import Network.HTTP.Client
    ( HttpExceptionContent(..)
    , Manager
    , ManagerSettings(..)
    , Request(..)
    , RequestBody(..)
    , method
    )

import qualified Control.Concurrent.MVar as MVar
import qualified Control.Exception.Safe as Exception
import qualified Control.Retry as Retry
import qualified Data.Aeson as Aeson
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding
import qualified Data.Text.Lazy as Text.Lazy
import qualified Data.Text.Lazy.Encoding as Lazy.Encoding
import qualified Network.HTTP.Types.Status as Status
import qualified Network.HTTP.Client as HTTP
import qualified Network.HTTP.Client.TLS as TLS
import qualified Network.HTTP.Types as HTTP.Types
import qualified OpenAI.V1 as OpenAI
import qualified Servant.Client as Client
import qualified System.IO.Unsafe as Unsafe

-- | Exception type thrown by `fetch` in the event of any failure
data HttpException
    = HttpException HTTP.HttpException
    | NotUTF8 UnicodeException
    deriving stock (Show)

instance Exception HttpException where
    displayException = Text.unpack . renderError

managerMVar :: MVar (Maybe Manager)
managerMVar = Unsafe.unsafePerformIO (MVar.newMVar Nothing)
{-# NOINLINE managerMVar #-}

retry :: IO a -> IO a
retry io =
    Retry.recovering
        retryPolicy
        [ \_ -> Handler handler
        ]
        (\_ -> io)
  where
    retryPolicy = Retry.fullJitterBackoff 1000000 <> Retry.limitRetries 3

    handler (FailureResponse _ Response{ responseStatusCode }) =
        return (Status.statusIsServerError responseStatusCode)
    handler (ConnectionError _) =
        return True
    handler _ =
        return False

-- | Acquire a new `Manager`
--
-- This is safe to call multiple times.  The `Manager` returned by the first
-- call is cached and reused by subsequent calls.
newManager :: IO Manager
newManager = MVar.modifyMVar managerMVar \maybeManager -> do
    manager <- case maybeManager of
        Nothing -> do
            TLS.newTlsManagerWith TLS.tlsManagerSettings
                { managerResponseTimeout = HTTP.responseTimeoutNone
                , managerRetryableException = \exception ->
                    case Exception.fromException exception of
                        Just (FailureResponse _ Response{ responseStatusCode }) ->
                            Status.statusIsServerError responseStatusCode
                        Just (ConnectionError _) ->
                            True
                        _ ->
                            False
                }

        Just manager -> do
            return manager

    return (Just manager, manager)

-- | Fetch a URL (using the @http-client@ package)
fetch
    :: Text
    -- ^ URL
    -> IO Text
    -- ^ Response body
fetch url = do
    manager <- newManager

    request <- HTTP.parseUrlThrow (Text.unpack url)

    let handler :: HTTP.HttpException -> IO a
        handler httpException = Exception.throwIO (HttpException httpException)

    response <- Exception.handle handler (HTTP.httpLbs request manager)

    let lazyBytes = HTTP.responseBody response

    case Lazy.Encoding.decodeUtf8' lazyBytes of
        Left exception -> Exception.throwIO (NotUTF8 exception)
        Right lazyText -> return (Text.Lazy.toStrict lazyText)

-- | Make a HTTP request
http :: Bool -> HTTP -> IO Text
http import_ GET{ url, headers, parameters } = do
    manager <- newManager

    renderedURL <- renderQueryText url parameters

    request₀ <- HTTP.parseUrlThrow (Text.unpack renderedURL)

    let request₁ = request₀
            { method = HTTP.Types.methodGet
            , requestHeaders = completeHeaders import_ False headers
            }

    let handler :: HTTP.HttpException -> IO a
        handler httpException = Exception.throwIO (HttpException httpException)

    response <- Exception.handle handler (HTTP.httpLbs request₁ manager)

    case Lazy.Encoding.decodeUtf8' (HTTP.responseBody response) of
        Left exception -> Exception.throwIO (NotUTF8 exception)
        Right lazyText -> return (Text.Lazy.toStrict lazyText)

http import_ POST{ url, headers, request } = do
    manager <- newManager

    request₀ <- HTTP.parseUrlThrow (Text.unpack url)

    let request₁ = request₀
            { method = HTTP.Types.methodPost
            , requestHeaders = completeHeaders import_ body headers
            }
          where
            body = case request of
                Nothing -> False
                Just _  -> True

    let request₂ = case request of
            Nothing ->
                request₁
            Just requestBody ->
                request₁{ requestBody = RequestBodyLBS (Aeson.encode requestBody) }

    let handler :: HTTP.HttpException -> IO a
        handler httpException = Exception.throwIO (HttpException httpException)

    response <- Exception.handle handler (HTTP.httpLbs request₂ manager)

    case Lazy.Encoding.decodeUtf8' (HTTP.responseBody response) of
        Left exception -> Exception.throwIO (NotUTF8 exception)
        Right lazyText -> return (Text.Lazy.toStrict lazyText)

-- | Render an `HttpException` as `Data.Text.Text`
renderError :: HttpException -> Text
renderError (HttpException httpException) = case httpException of
    HTTP.InvalidUrlException _ _ ->
        "Invalid URL"

    HTTP.HttpExceptionRequest _ e -> case e of
        ConnectionFailure _ ->
            "Remote host not found"
        InvalidDestinationHost _ ->
            "Invalid remote host name"
        ResponseTimeout ->
            "The remote host took too long to respond"
        ConnectionTimeout ->
            "Connection establishment took too long"
        StatusCodeException response body -> prefix <> suffix
          where
            statusCode =
                HTTP.Types.statusCode (HTTP.responseStatus response)

            prefix =
                case statusCode of
                    401 -> "Access unauthorized"
                    403 -> "Access forbidden"
                    404 -> "Remote file not found"
                    500 -> "Server-side failure"
                    502 -> "Upstream failure"
                    503 -> "Server temporarily unavailable"
                    504 -> "Upstream timeout"
                    _   -> "HTTP request failure"

            suffix =
                    "\n\
                    \\n\
                    \HTTP status code: " <> Text.pack (show statusCode) <> responseBody

            responseBody :: Text
            responseBody =
                case Encoding.decodeUtf8' body of
                    Left _ ->
                            "\n\
                            \\n\
                            \Response body (non-UTF8 bytes):\n\
                            \\n\
                            \" <> Text.pack (show body)
                    Right "" ->
                        ""
                    Right bodyText ->
                            "\n\n"
                        <>  "Response body:\n\
                            \\n\
                            \" <> prefixedText
                      where
                        prefixedLines =
                                zipWith combine prefixes
                                    (Text.lines bodyText)
                            <>  [ "…│ …" ]
                          where
                            prefixes = [(1 :: Int)..7]

                            combine n line =
                                Text.pack (show n) <> "│ " <> line

                        prefixedText = Text.unlines prefixedLines
        _ ->
           "HTTP request failure\n\
           \\n\
           \" <> Text.pack (displayException httpException)
renderError (NotUTF8 unicodeException) =
    "Not UTF8\n\
    \\n\
    \" <> Text.pack (displayException unicodeException)

-- | Initialize API for prompting
getMethods :: IO (Text -> Methods)
getMethods = do
    baseUrl <- Client.parseBaseUrl "https://api.openai.com"

    manager <- newManager

    let clientEnv = Client.mkClientEnv manager baseUrl

    return (\key -> OpenAI.makeMethods clientEnv key organization Nothing)

-- | This powers the @prompt@ keyword
createChatCompletion
    :: Methods
    -> CreateChatCompletion
    -> IO ChatCompletionObject
createChatCompletion Methods{ createChatCompletion = c } x = retry (c x)


================================================
FILE: ghc/Grace/Width.hs
================================================
{-| This module provides a uniform interface for getting the console width using
    both GHC and GHCJS
-}
module Grace.Width
    ( -- * Width
      getWidth
    , defaultWidth
    ) where

import System.Console.Terminal.Size (Window(..))

import qualified System.Console.Terminal.Size as Size

-- | Get the width of the terminal (in columns)
getWidth :: IO Int
getWidth = do
    maybeWindow <- Size.size

    let renderWidth = case maybeWindow of
            Nothing              -> defaultWidth
            Just Window{ width } -> width

    return renderWidth

-- | The default width to use
defaultWidth :: Int
defaultWidth = 80


================================================
FILE: ghcjs/Grace/DataFile.hs
================================================
{-| This module provides a uniform interface for accessing data files using
    both GHC and GHCJS
-}
module Grace.DataFile
    ( readDataFile
    ) where

import Data.Text (Text)

import qualified Data.Text as Text
import qualified Grace.HTTP as HTTP

-- | Read a data file by its relative path
readDataFile :: FilePath -> IO Text
readDataFile relativePath = do
    HTTP.fetch (Text.pack relativePath)


================================================
FILE: ghcjs/Grace/HTTP.hs
================================================
{-| This module provides a uniform interface for making HTTP requests using both
    GHC and GHCJS
-}
module Grace.HTTP
    ( HttpException
    , fetch
    , HTTP(..)
    , Header(..)
    , Parameter(..)
    , http
    , renderError
    , Methods
    , getMethods
    , createChatCompletion
    ) where

import Control.Exception.Safe (Exception(..))
import Data.Text (Text)
import GHCJS.Fetch.Types (JSResponse)
import OpenAI.V1.Chat.Completions (ChatCompletionObject, CreateChatCompletion)

import Grace.HTTP.Type
    ( Header(..)
    , HTTP(..)
    , Parameter(..)
    , completeHeaders
    , organization
    , renderQueryText
    )
import GHCJS.Fetch
    ( Request(..)
    , RequestCacheMode(..)
    , RequestOptions(..)
    , JSPromiseException
    )

import qualified Control.Exception.Safe as Exception
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as ByteString.Lazy
import qualified Data.JSString as JSString
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding
import qualified GHCJS.Fetch as Fetch
import qualified GHCJS.Prim as Prim
import qualified Network.HTTP.Types as HTTP.Types

-- | An `HttpException` is just a type synonym for a `JSPromiseException`
type HttpException = JSPromiseException

-- | Fetch a URL (using @XMLHttpRequest@)
fetch
    :: Text
    -- ^ URL
    -> IO Text
    -- ^ Response body
fetch url = do
    let request = Request
            { reqUrl = JSString.pack (Text.unpack url)
            , reqOptions = Fetch.defaultRequestOptions
                { reqOptMethod = HTTP.Types.methodGet
                , reqOptCacheMode = NoStore
                }
            }

    response <- Fetch.fetch request

    jsString <- Fetch.responseText response

    return (Text.pack (JSString.unpack jsString))

responseToText :: JSResponse -> IO Text
responseToText response = do
    jsString <- Fetch.responseText response

    return (Text.pack (JSString.unpack jsString))

-- | Make an HTTP request
http :: Bool -> HTTP -> IO Text
http import_ GET{ url, headers, parameters } = do
    newURL <- renderQueryText url parameters

    let reqUrl = JSString.pack (Text.unpack newURL)

    let reqOptions = Fetch.defaultRequestOptions
            { reqOptHeaders = completeHeaders import_ False headers
            , reqOptMethod = HTTP.Types.methodGet
            , reqOptCacheMode = NoStore
            }

    let request = Request{ reqUrl, reqOptions }

    response <- Fetch.fetch request

    responseToText response

http import_ POST{ url, headers, request } = do
    newURL <- renderQueryText url Nothing

    let reqUrl = JSString.pack (Text.unpack newURL)

    let reqOptions₀ = Fetch.defaultRequestOptions
            { reqOptHeaders = completeHeaders import_ body headers
            , reqOptMethod = HTTP.Types.methodPost
            , reqOptCacheMode = NoStore
            }
          where
            body = case request of
                Nothing -> False
                Just _  -> True

    reqOptions <- case request of
            Nothing -> do
                return reqOptions₀

            Just requestBody -> do
                requestText <- case Encoding.decodeUtf8' (ByteString.Lazy.toStrict (Aeson.encode requestBody)) of
                    Left exception -> Exception.throwIO exception
                    Right text -> return text

                let reqOptBody =
                        Just (Prim.toJSString (Text.unpack requestText))

                return reqOptions₀{ reqOptBody }

    response <- Fetch.fetch Request{ reqUrl, reqOptions }

    responseToText response

-- | Render an `HttpException` as `Data.Text.Text`
renderError :: HttpException -> Text
renderError = Text.pack . displayException

-- | The GHCJS implementation of OpenAI bindings just stores the API key
type Methods = Text

-- | Initialize API for prompting
getMethods :: IO (Text -> Methods)
getMethods = return id

-- | This powers the @prompt@ keyword
createChatCompletion
    :: Methods
    -> CreateChatCompletion
    -> IO ChatCompletionObject
createChatCompletion key createChatCompletion_ = do
    let keyBytes = Encoding.encodeUtf8 key

    body <- case Encoding.decodeUtf8' (ByteString.Lazy.toStrict (Aeson.encode createChatCompletion_)) of
        Left exception -> Exception.throwIO exception
        Right text -> return (Text.unpack text)

    let organizationHeader = case organization of
            Nothing -> []
            Just o  -> [("OpenAI-Organization", Encoding.encodeUtf8 o)]

    let request = Request
            { reqUrl = "https://api.openai.com/v1/chat/completions"
            , reqOptions = Fetch.defaultRequestOptions
                { reqOptMethod = "POST"
                , reqOptHeaders =
                    [ ("Content-Type", "application/json")
                    , ("Authorization", "Bearer " <> keyBytes)
                    ] <> organizationHeader
                , reqOptBody = Just (Prim.toJSString body)
                , reqOptCacheMode = NoStore
                }
            }

    response <- Fetch.fetch request

    jsString <- Fetch.responseText response

    let strictBytes = Encoding.encodeUtf8 (Text.pack (JSString.unpack jsString))

    let lazyBytes = ByteString.Lazy.fromStrict strictBytes

    case Aeson.eitherDecode lazyBytes of
        Left string ->
            fail string
        Right chatCompletionObject ->
            return chatCompletionObject


================================================
FILE: ghcjs/Grace/Size.hs
================================================
module Grace.Width
    ( -- * Width
      getWidth
    ) where

-- | Get the width of the terminal (in columns)
getWidth :: IO Int
getWidth = pure 80


================================================
FILE: ghcjs/Grace/Width.hs
================================================
{-| This module provides a uniform interface for getting the console width using
    both GHC and GHCJS
-}
module Grace.Width
    ( -- * Width
      getWidth
    , defaultWidth
    ) where

-- | Get the width of the terminal (in columns)
getWidth :: IO Int
getWidth = pure defaultWidth

-- | The default column size to use
defaultWidth :: Int
defaultWidth = 80


================================================
FILE: grace/Main.hs
================================================
module Main where

import qualified Grace

main :: IO ()
main = Grace.main


================================================
FILE: grace.cabal
================================================
cabal-version:       >=1.10
name:                grace
version:             1.0.0
license-file:        LICENSE
author:              Gabriella Gonzalez
maintainer:          Gabriel439@gmail.com
build-type:          Simple
extra-source-files:  examples/*.ffg
                     examples/tutorial/*.ffg
                     prompts/*.md
                     prelude/bool/*.ffg
                     prelude/integer/*.ffg
                     prelude/list/*.ffg
                     prelude/natural/*.ffg
                     prelude/package.ffg
                     prelude/real/*.ffg
                     prelude/text/*.ffg

library
  hs-source-dirs:      src

  build-depends:       base
                     , Earley
                     , aeson
                     , aeson-pretty
                     , aeson-yaml
                     , async
                     , binary
                     , bytestring
                     , containers
                     , case-insensitive
                     , directory
                     , exceptions
                     , filepath
                     , generic-lens
                     , http-types
                     , insert-ordered-containers
                     , lens
                     , megaparsec
                     , modern-uri
                     , mmorph
                     , mtl
                     , openai
                     , parser-combinators
                     , prettyprinter
                     , prettyprinter-ansi-terminal
                     , safe-exceptions
                     , scientific
                     , template-haskell
                     , text >= 1.2.4.0
                     , time
                     , unordered-containers
                     , vector

  exposed-modules:     Grace.Aeson
                     , Grace.Context
                     , Grace.DataFile
                     , Grace.Decode
                     , Grace.Domain
                     , Grace.Encode
                     , Grace.Existential
                     , Grace.GitHub
                     , Grace.HTTP
                     , Grace.Import
                     , Grace.Infer
                     , Grace.Input
                     , Grace.Interpret
                     , Grace.Label
                     , Grace.Location
                     , Grace.Monad
                     , Grace.Monotype
                     , Grace.Normalize
                     , Grace.Parallelizable
                     , Grace.Parser
                     , Grace.Pretty
                     , Grace.Prompt
                     , Grace.Prompt.Types
                     , Grace.Syntax
                     , Grace.Type
                     , Grace.Value
                     , Grace.Width

  other-modules:       Grace.Compat
                     , Grace.HTTP.Type
                     , Grace.Marshal
                     , Paths_grace

  default-extensions:  ApplicativeDo
                     , BangPatterns
                     , BlockArguments
                     , DataKinds
                     , DefaultSignatures
                     , DeriveAnyClass
                     , DeriveFoldable
                     , DeriveFunctor
                     , DeriveGeneric
                     , DeriveLift
                     , DeriveTraversable
                     , DerivingStrategies
                     , DuplicateRecordFields
                     , FlexibleContexts
                     , FlexibleInstances
                     , GeneralizedNewtypeDeriving
                     , KindSignatures
                     , MultiParamTypeClasses
                     , MultiWayIf
                     , NamedFieldPuns
                     , OverloadedStrings
                     , ScopedTypeVariables
                     , StandaloneDeriving
                     , TypeApplications
                     , TypeOperators
                     , UndecidableInstances
                     , ViewPatterns

  default-language:    Haskell2010

  ghc-options:         -Wall -Wmissing-deriving-strategies

  if impl(ghcjs) || os(ghcjs)
    hs-source-dirs:    ghcjs
    build-depends:     ghcjs-base
                     , ghcjs-fetch
                     , ghcjs-prim
  else
    hs-source-dirs:    ghc
    build-depends:     ansi-terminal
                     , haskeline
                     , http-client
                     , http-client-tls
                     , optparse-applicative
                     , repline
                     , retry
                     , servant-client-core
                     , servant-client
                     , terminal-size
    exposed-modules:   Grace
                     , Grace.REPL
                     , Grace.TH

executable grace
  main-is:             Main.hs

  build-depends:       base
                     , grace

  hs-source-dirs:      grace

  default-language:    Haskell2010

  ghc-options:         -threaded -rtsopts -with-rtsopts=-N

  if impl(ghcjs) || os(ghcjs)
    buildable:         False

executable try-grace
  main-is:             Main.hs

  build-depends:       base
                     , aeson
                     , async
                     , containers
                     , filepath
                     , ghcjs-base
                     , grace
                     , insert-ordered-containers
                     , lens
                     , mmorph
                     , mtl
                     , safe-exceptions
                     , scientific
                     , stm
                     , text
                     , transformers
                     , uri-encode

  hs-source-dirs:      try-grace

  ghc-options:         -Wall

  default-language:    Haskell2010
  if !(impl(ghcjs) || os(ghcjs))
    buildable:         False

test-suite tasty
  type:                exitcode-stdio-1.0

  main-is:             Main.hs

  build-depends:       base
                     , aeson
                     , containers
                     , directory
                     , filepath
                     , grace
                     , mtl
                     , prettyprinter
                     , safe-exceptions
                     , scientific
                     , tasty
                     , tasty-hunit
                     , tasty-silver
                     , text
                     , vector

  hs-source-dirs:      tasty

  default-language:    Haskell2010

  ghc-options:         -Wall

test-suite doctest
  type:                exitcode-stdio-1.0

  main-is:             Main.hs

  build-depends:       base
                     , doctest

  hs-source-dirs:      doctest

  default-language:    Haskell2010

  ghc-options:         -Wall


================================================
FILE: prelude/bool/and.ffg
================================================
# Returns `true` if there are no `false`s in the input `List` and `false`
# otherwise
let and
      : List Bool -> Bool
      = fold { cons: \x -> \y -> x && y, nil: true }

in  and


================================================
FILE: prelude/bool/not.ffg
================================================
# Negate a `Bool`
let not (b : Bool) : Bool = b == false

in  not


================================================
FILE: prelude/bool/or.ffg
================================================
# Returns `false` if there are no `true`s in the input `List` and `true`
# otherwise
let or
      : List Bool -> Bool
      = fold { cons: \x -> \y -> x || y, nil: false }

in  or


================================================
FILE: prelude/bool/package.ffg
================================================
{ and: ./and.ffg
, not: ./not.ffg
, or: ./or.ffg
}


================================================
FILE: prelude/integer/clamp.ffg
================================================
# Convert an `Integer` to a `Natural` number, clamping negative values to `0`
let clamp (n : Integer) : Natural = if n < 0 then 0 else abs n

in  clamp


================================================
FILE: prelude/integer/package.ffg
================================================
{ clamp: ./clamp.ffg
, product: ./product.ffg
, sum: ./sum.ffg
, toNatural: ./toNatural.ffg
}


================================================
FILE: prelude/integer/product.ffg
================================================
# Multiply the elements of a list
let product
      : List Integer -> Integer
      = fold { cons: \x y -> x * y : Integer, nil: 1 : Integer }

in  product


================================================
FILE: prelude/integer/sum.ffg
================================================
# Add up the elements of a list
let sum
      : List Integer -> Integer
      = fold { cons: \x y -> x + y : Integer, nil: 0 : Integer }

in  sum


================================================
FILE: prelude/integer/toNatural.ffg
================================================
# Convert an `Integer` to a `Natural` number, returning `null` if the `Natural`
# number is negative
let toNatural (n : Integer) : Optional Natural =
        if n < 0 then null else abs n

in  toNatural


================================================
FILE: prelude/list/all.ffg
================================================
# Check if all elements satisfy the given predicate
let all
      : forall (a : Type) . (a -> Bool) -> List a -> Bool
      = \predicate ->
          fold
            { cons: \element -> \result -> predicate element && result
            , nil: true
            }

in  all


================================================
FILE: prelude/list/any.ffg
================================================
# Check if any elements satisfy the given predicate
let any
      : forall (a : Type) . (a -> Bool) -> List a -> Bool
      = \predicate ->
          fold
            { cons: \element -> \result -> predicate element || result
            , nil: false
            }

in  any


================================================
FILE: prelude/list/concat.ffg
================================================
let concat
      : forall (a : Type) . List (List a) -> List a
      = \lists ->
          for list of lists

          for element of list

          in  element

in  concat


================================================
FILE: prelude/list/generate.ffg
================================================
# Build a `List` by calling the supplied function on all `Natural` numbers from
# `0` up to but not including the supplied `Natural` number
let replicate = ./replicate.ffg

let generate
      : forall (a : Type) . Natural -> (Natural -> a) -> List a
      = \n -> \f -> map (\x -> f x.index) (indexed (replicate n { }))

in  generate


================================================
FILE: prelude/list/null.ffg
================================================
# Returns `true` if the `List` is empty and `false` otherwise
let null_
      : forall (a : Type) . List a -> Bool
      = \xs -> length xs == 0

in  null_


================================================
FILE: prelude/list/package.ffg
================================================
{ all: ./all.ffg
, any: ./any.ffg
, concat: ./concat.ffg
, generate: ./generate.ffg
, null: ./null.ffg
, reject: ./reject.ffg
, replicate: ./replicate.ffg
, select: ./select.ffg
}


================================================
FILE: prelude/list/reject.ffg
================================================
# Only keep elements of the list where the supplied functions returns `false`
let reject
      : forall (a : Type) . (a -> Bool) -> List a -> List a
      = \f ->
          fold
            { "cons": \x -> \xs -> if f x then xs else [ x ] + xs, "nil": [ ] }

in  reject


================================================
FILE: prelude/list/replicate.ffg
================================================
# Create a `List` from `n` copies of an element
let replicate
      : forall (a : Type) . Natural -> a -> List a
      = \n x -> fold { succ: \xs -> [ x ] + xs, zero: [] } n

in  replicate


================================================
FILE: prelude/list/select.ffg
================================================
# Only keep elements of the list where the supplied functions returns `true`
let select
      : forall (a : Type) . (a -> Bool) -> List a -> List a
      = \f ->
          fold
            { "cons": \x -> \xs -> if f x then [ x ] + xs else xs, "nil": [ ] }

in  select


================================================
FILE: prelude/natural/package.ffg
================================================
{ product: ./product.ffg
, sum: ./sum.ffg
}


================================================
FILE: prelude/natural/product.ffg
================================================
# Multiply the elements of a list
let product
      : List Natural -> Natural
      = fold { cons: \x y -> x * y : Natural, nil: 1 : Natural }

in  product


================================================
FILE: prelude/natural/sum.ffg
================================================
# Add up the elements of a list
let sum
      : List Natural -> Natural
      = fold { cons: \x y -> x + y : Natural, nil: 0 : Natural }

in  sum


================================================
FILE: prelude/package.ffg
================================================
{ bool: ./bool/package.ffg
, real: ./real/package.ffg
, integer: ./integer/package.ffg
, list: ./list/package.ffg
, natural: ./natural/package.ffg
, text: ./text/package.ffg
}


================================================
FILE: prelude/real/package.ffg
================================================
{ product: ./product.ffg
, sum: ./sum.ffg
}


================================================
FILE: prelude/real/product.ffg
================================================
# Multiply the elements of a list
let product
      : List Real -> Real
      = fold { cons: \x y -> x * y : Real, nil: 1 : Real }

in  product


================================================
FILE: prelude/real/sum.ffg
================================================
# Add up the elements of a list
let sum
      : List Real -> Real
      = fold { cons: \x y -> x + y : Real, nil: 0 : Real }

in  sum


================================================
FILE: prelude/text/concat.ffg
================================================
# Concatenate a `List` of `Text`
let concat
      : List Text -> Text
      = fold { cons: \x -> \y -> (x + y) : Text, nil: "" }

in  concat


================================================
FILE: prelude/text/concatMap.ffg
================================================
# Transform each value in a `List` into `Text` and concatenate the result
let concatMap
      : forall (a : Type) . (a -> Text) -> List a -> Text
      = \f -> fold { cons: \x -> \y -> f x + y, nil: "" }

in  concatMap


================================================
FILE: prelude/text/concatMapSep.ffg
================================================
# Concatenate a `List` of `Text` values with a separator in between each value
let concatSep = ./concatSep.ffg

let concatMapSep
      : forall (a : Type) . Text -> (a -> Text) -> List a -> Text
      = \separator -> \f -> \xs -> concatSep separator (map f xs)

in  concatMapSep


================================================
FILE: prelude/text/concatSep.ffg
================================================
# Concatenate a `List` of `Text` values with a separator in between each value
let concatSep (separator : Text) (xs : List Text) : Text =
      let status
            = fold
                { "cons": \element ->
                            fold
                              { "null": some element
                              , "some": \result ->
                                          some (element + separator + result)
                              }
                , "nil": null
                }
                xs

      in  fold { "null": "", "some": \result -> result } status

in  concatSep


================================================
FILE: prelude/text/package.ffg
================================================
{ concat: ./concat.ffg
, concatMap: ./concatMap.ffg
, concatMapSep: ./concatMapSep.ffg
, concatSep: ./concatSep.ffg
}


================================================
FILE: prompts/abnf.md
================================================
Here's the grammar for Grace if you're not exactly sure what the language does
and does not permit so that you don't run into trivial syntax errors:

```
expression
  = lambda
  / let
  / if
  / annotation  ; Everything else (operators, projections, literals, etc.)

identifier
  ; Naked variable names begin with a lowercase letter or "_"
  ;
  ; Examples: `x`, `name`, `toLicense`
  = (LOWER / "_") *(ALPHANUM / "_" / "-" / "/")

  ; Quoted variable names begin with `.` and are surrounded with single quotes
  ;
  ; Examples: `.'Rationale'`, `.'Section Header'`, `.'Website - Backend'`
  / "." single-quoted

lambda = "\" 1*binding "->" expression

; A bound variable with an optional type annotation and optional default value
name-binding = identifier [ ":" type ] [ "=" expression ]

; One or more names bound as arguments for a lambda or named functions
binding
  ; Plain bound variable:
  ;
  ;     \x -> …
  ;
  ;     let f x = … in …
  ;
  ;     let greet .'Name' = "Hi, ${.'Name'}!" in greet "Alice"
  ;
  = identifier

  ; Bound variable with an optional type annotation and optional default value:
  ;
  ;     \(x : T = v) -> …
  ;
  ;     let f (x : T = v) = … in …
  ;
  ;     let greet (.'Name' : Text = "Alice") = "Hi, ${.'Name'}!" in greet null
  ;
  ; You can have just the type annotation:
  ;
  ;     \(x : T) -> …
  ;
  ;     let f (x : T) = … in …
  ;
  ; … or just the default value:
  ;
  ;     \(x = v) -> …
  ;
  ;     let f (x = v) = … in …
  ;
  ;     let greet (.'Name' : Text) = "Hi, ${.'Name'}!" in greet "Alice"
  ;
  ; You can even omit both and just parenthesize the bound variable, although
  ; this is not idiomatic since you'd usually omit the parentheses in that case:
  ;
  ;     \(x) -> …
  ;
  ;     let f (x) = … in …
  ;
  ;     let greet (.'Name') = "Hi, ${.'Name'}!" in greet "Alice"
  ;
  / "(" name-binding ")"

  ; Destructure a record function argument:
  ;
  ;     \{ a, b } -> …
  ;
  ;     let f{ a, b } = … in …
  ;
  ;     let greet{ "Name" } = "Hi, ${.'Name'}!" in greet{ "Name": "Alice" }
  ;
  ;     let greet{ .'Name' } = "Hi, ${.'Name'}!" in greet{ "Name": "Alice" }
  ;
  ; Record fields destructured in this way can have optional type annotations
  ; and optional default values:
  ;
  ;     \{ a, b : T0, c = v0, d : T1 = v1 } -> …
  ;
  ;     let f { a, b : T0, c = v0, d : T1 = v1 } = … in …
  ;
  ;     let greet{ "Name" : Text = "Alice" } = "Hi, ${.'Name'}!" in greet{ }
  ;
  ;     let greet{ .'Name' : Text = "Alice" } = "Hi, ${.'Name'}!" in greet{ }
  / "{" [ name-binding *( "," name-binding ) ] "}"

; Note: Every sequence of `let`s (even top-level `let`s) must have a matching
; `in`.  Dangling `let`s are a parse error in any context.
;
; BAD:
;
;     let x = 2
;     let y = 3  # Missing `in` at the end, which is a syntax error
;
; GOOD:
;
;     let x = 2
;     let y = 3
;     in  { x, y }  # To "export" let bindings, package them in a record
let = 1*assignment "in" expression

; Every assignment must begin with a `let` because Grace is not
; whitespace-sensitive.
;
; BAD:
;
;     let x = 2
;         y = 3  # Missing `let`, so this is misparsed as `let x = 2 y = 3`
;     in  x + y
;
; GOOD:
;
;     let x = 2
;     let y = 3
;     in  x + y
assignment =
    ; Define a simple value:
    ;
    ;     let x = 2 in x + 1
    ;
    ; … or a function of one or more arguments:
    ;
    ;     let increment x = x + 1 in increment 2
    ;
    ; Function definitions can destructure their arguments and this is the most
    ; idiomatic way to define functions in Grace:
    ;
    ;     let greet{ name } = "Hi, ${name}!" in greet{ name: "Alice" }
    = ("let" identifier *binding [ ":" type ] "=" expression)

    ; Destructure the right-hand side:
    ;
    ;     let { x, y } = { x: 1, y: 2 } in x + y
    / ("let" binding "=" expression)

    ; For comprehension (like a list comprehension, but works on both `List`s
    ; and `Optional`s)
    ;
    ;     for x of [ 1, 2 ]
    ;     for y of [ false, true ]
    ;     in  { x, y }
    ;
    ;     = [ {x: 1, y: false }
    ;       , {x: 1, y: true  }
    ;       , {x: 2, y: false }
    ;       , {x: 2, y: true  }
    ;       ]
    / ("for" binding "of" expression)

if = "if" expression "then" expression "else" expression

; Optional type annotation:
;
;     e : T
annotation = application *( operator application ) [ ":" type ]

; Operators in descending order of precedence
;
; This is the same precedence order as C operators
operator
    = "/"
    / "%"
    / "*"
    / "-"
    / "+"
    / ">="
    / ">"
    / "<="
    / "<"
    / "!="
    / "=="
    / "&&"
    / "||"

application
  ; Keyword to prompt an LLM to generate a JSON value (the default) or Grace
  ; code (when preceded with `import`)
  = [ "import" ] "prompt" projection

  ; Keyword to make an HTTP request to fetch a JSON value (the default) or Grace
  ; code (when preceded with `import`)
  = [ "import" ] "http" projection

  ; Keyword to convert text to a JSON value (the default) or Grace code (when
  ; preceded with `import`)
  = [ "import" ] "read" projection

  ; Keyword to fetch a JSON file from GitHub (the default) or Grace code (when
  ; preceded with `import`)
  = [ "import" ] "github" projection

  ; Keyword to render a JSON expression (the default) or any expression (when
  ; preceded with `export`) as text
  = [ "export" ] "show" projection

  ; Keyword to pattern match on a union
  / "fold" projection

  ; Ordinary function application (left-associative)
  / application projection

  / projection

; Optionally project a field (from a record) or an element (from a list)
projection = primitive *smaller

smaller
  ; You can access a record field using `record.field`.
  = "." field

  ; You can project multiple fields from a record
  / "." "{" [ field *( "," field ) ] "}"

  ; You can also index into a list using dot notation (e.g. `list.index`)  Just
  ; like Python, you can index from the end of the list using negative numbers
  ; (e.g. `list.-1` to get the last element of the list).
  / "." integer

  ; You can slice into a list using `xs[m:n]` just like in Python.  Slice
  ; indices may also be negative and both indices are optional.
  / "[" [ integer ] ":" [ integer ] "]"

primitive
  = identifier  ; bound variable (e.g. `x`)
  / "null"
  / boolean
  / number
  / string
  / builtin
  / list
  / record

  ; NOTE: all alternatives need an argument.  If an alternative is empty you
  ; still need to store an empty record inside of it (e.g. `Foo{ }`)
  / alternative primitive

  ; An absolute path (beginning with `/`) or relative path (beginning with `../`
  ; or `./`)
  / file

  ; A URI (supported schemes: `https` / `http` / `env` / `file`)
  / uri

  / "(" expression ")"

boolean = "true" / "false"

number = natural / integer / real

; Positive integers are parsed as `Natural` numbers
natural = 1*DIGIT

; Signed integers are parsed as `Integer`s
integer = ("+" / "-") natural

; All other numbers are parsed as `Real`s
real = [ ( "+" / "-" ) ] 1*DIGIT "." 1*DIGIT

; Strings support two modes:
;
; - Single-line string literals, like:
;
;       "abc"
;
; - Multi-line string literals, like:
;
;       "
;       Line 1
;
;       Line 3
;       "
string = single-line-string / multi-line-string

; Single-line string literals only support escaped newlines (i.e. `\n`)
single-line-string =
  %x22 *( single-line-character / interpolation / single-line-escape) %x22

; A character other than " or \
single-line-character = %x20-21 / %x23-5B / %x5D-10FFFF

; NOTE: You can escape a string interpolation using a backslash like this:
;
; ```
; "Example: \${…}"
; ```
;
; … if you don't want Grace to interpret the string interpolation.  This comes
; in handy if you, say, want to use Grace to generate a Bash script without
; interpreting Bash string interpolations.
single-line-escape =
  "\\" ( %x22 / "\\" / "/" / "n" / "t" / "r" / "b" / "f" / "$" / ("u" 4HEXDIG) )

; These string literals can span multiple lines and leading indentation is
; stripped.  For example, this:
;
;     let example =
;             "
;             Line 1
;
;             Line 3
;             "
;
; … is the same thing as:
;
;     let example = "Line 1\n\nLine 3\n"
;
; The difference between a single-line string literal and a multi-line string
; literal is that in a multi-line string literal the `"` is followed by a
; newline (which must be present and is stripped).  For example, this:
;
;     let example =
;             "
;             Line 1"
;
; … is the same thing as:
;
;     let example = "Line 1"
multi-line-string =
  %x22 %x0A *( multi-line-character / interpolation / multi-line-escape) %x22

; A character other than " or \
;
; Literal tabs and newlines are also permitted, unlike single-line strings.
multi-line-character = %x09-0A / %x20-21 / %x23-5B / %x5D-10FFFF

; NOTE: You cannot escape newlines or tabs in a multi-line string literal
; (because you can and should use an actual newline or tab character instead of
; an escaped one).
multi-line-escape =
  "\\" ( %x22 / "\\" / "/" / "r" / "b" / "f" / "$" / ("u" 4HEXDIG) )

; Interpolated expressions must have type `Text`.  Grace does *not* perform
; any automatic conversion of interpolated values to `Text`.  If you want to
; interpolate a number, then use:
;
; ```
; "… ${show number} …"
; ```
;
; Interpolated expressions do not need to be escaped:
;
; BAD:
;
;     \input -> "Hello, ${input.\"First Name\"}!"
;
; GOOD:
;
;     \input -> "Hello, ${input."First Name"}!"
interpolation = "${" expression "}"

; A name for one of the alternatives of a union
alternative
  ; Unquoted alternative names begin with an uppercase letter
  = UPPER *(ALPHANUM / "_" / "-" / "/")

  ; Quoted alternative names are surrounded with single quotes
  / single-quoted

single-quoted = "'" (single-quoted-character / single-quoted-escape) "'"

; A character other than ' or \
single-quoted-character = %x20-26 / %x28-5B / %x5D-10FFFF

; Similar to the rule for "escape" except replacing " with ' and also not
; including an escape sequence for $ (since it's not necessary because a quoted
; alternative name can't include an interpolation).
single-quoted-escape =
  "\\" ( "'" / "\\" / "/" / "n" / "t" / "r" / "b" / "f" / ("u" 4HEXDIG) )

; Lists allow optional leading/trailing commas.
list = "[" [ "," ] [ expression *( "," expression ) ] [ "," ] "]"

; Records allow optional leading/trailing commas
record = "{" [ "," ] [ projection-value *( "," projection-value ) ] [ "," ] "}"

projection-value
  ; Grace uses JSON syntax for projection values: ':' (not '=')
  = field ":" expression

  ; Field punning.  In other words, `{ x }` is the same thing as `{ x: x }`
  / field

field
  = identifier

  ; Field names can be alternative names, too.  This is necessary so that you
  ; can `fold` unions (since the field names need to match the union's
  ; alternative names)
  / alternative

  ; You can quote field names, too, which comes in handy if a field has
  ; characters that would otherwise be forbidden (e.g. spaces or punctuation)
  ; (e.g. `record."Example field"` or `{ "Example field": true }`)
  / string

builtin
    = "yaml"     ; JSON -> Text  ; Renders argument as YAML
    / "indexed"  ; forall (a : Type) . List a -> List { index: Natural, value: a }
    / "length"   ; forall (a : Type) . List a -> Natural
    / "map"      ; forall (a : Type) (b : Type) . (a -> b) -> List a -> List b
    / "abs"      ; Integer -> Natural
    / "reveal"   ; Key -> Text
    / "some"     ; forall (a : Type) . a -> Optional a

type = quantified-type

quantified-type = *forall function-type

forall = "forall" 1*( "(" identifier ":" domain ")" ) "." 
domain = "Type" / "Fields" / "Alternatives"

function-type = application-type *( "->" application-type )

application-type
  = "List" primitive-type
  / "Optional" primitive-type
  / primitive-type

primitive-type
  = identifier  ; Type variable
  / "Bool"
  / "Real"
  / "Integer"
  / "Natural"
  / "Text"
  / "JSON"
  / "Key"
  / record-type
  / union-type
  / "(" type ")"

; Records types allow optional leading/trailing commas
record-type =
    "{"
    [ "," ]
    [ field ":" type *( "," field ":" type ) ]
    [ "," identifier ]  ; Fields variable (e.g. `{ x: Text, other }`
    [ "," ]
    "}"

; Union types allow optional leading/trailing bars
union-type =
    "<"
    [ "|" ]
    [ alternative ":" type *( "|" alternative ":" type ) ]
    [ "|" identifier ]  ; Alternatives variable (e.g. `< Left: Natural | other >`)
    [ "|" ]
    ">"
```


================================================
FILE: prompts/inference.md
================================================
At [work](https://mercury.com/jobs) I've been researching how to improve the ergonomics of prompt chaining and I wanted to share and open source some of what I've done.  This initial post is about how I've been experimenting with using *bidirectional type inference* to streamline *prompt chaining*.

"Prompt chaining" is a prompt engineering technique that splits a larger task/prompt into multiple smaller tasks/prompts which are chained together using code.  For example, instead of prompting a model to generate a poem in one prompt like this:

> Write a poem based off this idea:
> 
> `${idea}`
> 
> … by following this process:
> 
> - First think through the form, stanza count, lines per stanza, and rhyme scheme
> - Then choose a poetic style (tone, voice, and literary devices) based on the poem's form
> - Then write a complete poem based on that plan

… you can split it into smaller prompts, like this:

##### `structure` prompt:

> Plan the structure of a new poem based on this idea
> 
> `${idea}`
> 
> Describe its form, stanza count, lines per stanza, and rhyme scheme

##### `style` prompt:

> Given this poem structure:
> 
> - Form: `${structure."Form"}`
> - Stanzas: `${structure."Stanza Count"}`
> - Lines per stanza: `${structure."Lines per Stanza"}`
> - Rhyme scheme: `${structure."Rhyme Scheme"}`
> 
> Choose a poetic style: tone, voice, and literary devices to emphasize

##### `poem` prompt:

> Write a complete poem based on this idea:
> 
> `${idea}`
> 
> Structure:
> - Form: `${structure."Form"}`
> - Stanzas: `${structure."Stanza Count"}`
> - Lines per stanza: `${structure."Lines per Stanza"}`
> - Rhyme scheme: `${structure."Rhyme Scheme"}`
> 
> Style:
> - Tone: `${style."Tone"}`
> - Voice: `${style."Voice"}`
> - Literary Devices: `${style."Literary Devices"}`

Why might you want to do this?

- to improve the quality of the results
  
  Models perform better when working on more constrained subproblems.  Splitting a larger prompt into smaller prompts helps the model stay focused at each step.

- to introspect intermediate results
  
  This comes in handy when you want to log, validate, or correct intermediate results.

- to perform actions in between prompts
  
  You might want to take the output of one prompt, use that to call some tool, then use the output of that tool to decide what the next prompt should be, which you can't do with a single prompt.

In other words, prompt chaining unlocks greater *accuracy*, *control*, and *flexibility* for prompt engineering.

#### The problem

The main issue with prompt chaining is that it is a huge pain in the ass; if you start do anything a little bit complicated you need to start using structured outputs (i.e. JSON), which adds a whole lot of boilerplate to the process:

- you have to define the schema for each intermediate step of the process
  
  You typically do this by defining your data model in your host programming language (e.g. a Pydantic model in Python) or directly defining your JSON schema

- You have to instruct the model to produce JSON and explain the shape of the expected output

- (Depending on the framework) you have to decode the JSON into your data model

For small prompt chaining pipelines this isn't too hard, but it starts to get annoying to define all these schemas when you scale this up to more sophisticated prompt chaining pipelines.

So as a thought experiment I wanted to create a research prototype that handled all of that for you so that you didn't need to specify any schemas at all.  In other words I wanted to build a programming language that harnessed *bidirectional type inference* to perform *schema inference* for prompts with structured JSON outputs.

#### Example

I'll cut to the case by showing the above prompt chain written as a program in this language:

```haskell
\{ key } ->

let concatSep = import github
      { owner: "Gabriella439"
      , repository: "grace"
      , path: "prelude/text/concatSep.ffg"
      }

let generatePoem idea =
        let structure = prompt
                { key
                , text: "
                    Plan the structure of a new poem based on this idea:

                    ${idea}

                    Describe its form, stanza count, lines per stanza, and rhyme scheme.
                    "
                }

        let renderedStructure = "
                - Form: ${structure."Form"}
                - Stanzas: ${show (structure."Stanza Count": Natural)}
                - Lines per stanza: ${show (structure."Lines per Stanza" : Natural)}
                - Rhyme scheme: ${structure."Rhyme Scheme"}
                "

        let style = prompt
                { key
                , text: "
                    Given this poem structure:

                    ${renderedStructure}

                    Choose a poetic style: tone, voice, and literary devices to emphasize.
                    "
                }

        let renderedStyle = "
                - Tone: ${style."Tone"}
                - Voice: ${style."Voice"}
                - Literary Devices: ${concatSep ", " style."Literary Devices"}
                "

        let poem = prompt
                { key
                , text: "
                    Write a complete poem based on this idea:

                    ${idea}

                    Structure:

                    ${renderedStructure}

                    Style:

                    ${renderedStyle}
                    "
                }

        in { structure, style, poem }

in  generatePoem "The stillness of early morning"
```

The above prompt chain is a real program in an real (albeit research) [programming language named Fall-from-Grace](https://github.com/Gabriella439/grace) (or "Grace" for short).  Specifically, this is implemented in a [branch of the language](https://github.com/Gabriella439/grace/tree/gabriella/llm) that adds a magic `prompt` keyword that works like in the above example.

If you run the above example, you might get an output like this:

```json
{
  "structure": {
    "Form": "Free verse with a sprinkle of rhyme",
    "Lines per Stanza": 4,
    "Rhyme Scheme": "ABCB",
    "Stanza Count": 5
  },
  "style": {
    "Literary Devices": [
      "imagery",
      "alliteration",
      "personification",
      "enjambment"
    ],
    "Tone": "nostalgic",
    "Voice": "reflective"
  },
  "poem": "### The Stillness of Early Morning\n\n**I**  \nIn the gentle rise of dawn's embrace,  \nA whisper of winds through sleepy skies,  \nThe world lies suspended in an echo  \nOf yesterday's dreams, reluctantly saying goodbye.\n\n**II**  \nSoft orange light graces fields of dew,  \nCradling tenacious tendrils of the past.  \nMorning breathes upon the waiting world  \nWith promises that this serenity will last.\n\n**III**  \nWaves of silence surge across the meadow,  \nNature's symphony gently cradles rest.  \nA chorus of leaves sings its soft refrain,  \nComforted by morning's tender caress.\n\n**IV**  \nThe silence dips to caress weary trees,  \nWhose branches waltz to whispers soft and slow.  \nEach tendril holds close the morning's intention,  \nBefore the day's demands, the chaos, start to show.\n\n**V**  \nEarly rays dance upon a tranquil sea,  \nA canvas painted through a dreamy haze.  \nTime sits by, savoring the gentle hush,  \nAs the world awakens, stirs, and finally plays."
}
```

… and if you're curious what the formatted poem looks like:

### The Stillness of Early Morning

**I**  
In the gentle rise of dawn's embrace,  
A whisper of winds through sleepy skies,  
The world lies suspended in an echo  
Of yesterday's dreams, reluctantly saying goodbye.

**II**  
Soft orange light graces fields of dew,  
Cradling tenacious tendrils of the past.  
Morning breathes upon the waiting world  
With promises that this serenity will last.

**III**  
Waves of silence surge across the meadow,  
Nature's symphony gently cradles rest.  
A chorus of leaves sings its soft refrain,  
Comforted by morning's tender caress.

**IV**  
The silence dips to caress weary trees,  
Whose branches waltz to whispers soft and slow.  
Each tendril holds close the morning's intention,  
Before the day's demands, the chaos, start to show.

**V**  
Early rays dance upon a tranquil sea,  
A canvas painted through a dreamy haze.  
Time sits by, savoring the gentle hush,  
As the world awakens, stirs, and finally plays.

#### Type inference

The sample Grace program hardly specifies any types (mainly the final expected type for the `poem`: `Text`).  The reason this works is because Grace supports *bidirectional type inference*, which means that Grace can work backwards from how intermediate results are used to infer their schemas.

I'll illustrate this with a contrived Grace example:

```haskell
\{ key } ->

let { x, y } = prompt{ key, text: "Give me two numbers" }

in  { x, y, sum: x + y : Integer }
```

… which might produce an output like this:

```bash
$ grace interpret ./numbers.ffg
```
```json
{ "x": 7, "y": 14, "sum": 21 }
```

When Grace analyzes this program the type checker works backwards from this expression:

```haskell
x + y : Integer
```

… and reasons about it like this:

- the addition produces an `Integer`, therefore `x` and `y` must also be `Integer`s

- therefore the output of the `prompt` function must be a record with two `Integer` fields: `x` and `y`
  
… and then Grace generates a JSON schema for the prompt which looks like this:

```json
{ "type": "object",
  "properties": {
    "x": { "type": "integer" },
    "y": { "type": "integer" }
  },
  "required": [ "x", "y" ],
  "additionalProperties": false
}
```

Of course, you *can* specify types if you want (and they're more lightweight than schemas in traditional prompt chaining frameworks).  For example:

```bash
$ grace repl
>>> prompt{ key: ./openai.key, text: "Give me a first and last name" } : { first: Text, last: Text }
{ "first": "Emily", "last": "Johnson" }
>>> prompt{ key: ./openai.key, text: "Give me a list of names" } : List Text
[ "Alice"
, "Bob"
, "Charlie"
, "Diana"
, "Ethan"
, "Fiona"
, "George"
, "Hannah"
, "Isaac"
, "Jack"
]
```

However in our original example we don't need to specify intermediate types because when the type-checker sees this code:

```haskell
let structure = prompt
        { key
        , text: "
            Plan the structure of a new poem based on this idea:

            ${idea}

            Describe its form, stanza count, lines per stanza, and rhyme scheme.
            "
        }

let renderedStructure = "
        - Form: ${structure."Form"}
        - Stanzas: ${show (structure."Stanza Count" : Natural)}
        - Lines per stanza: ${show (structure."Lines per Stanza" : Natural)}
        - Rhyme scheme: ${structure."Rhyme Scheme"}
        "
```

… the compiler can reason backwards from how the `structure` value is used to infer that the JSON schema for the `prompt` needs to be:

```json
{ "type": "object",
  "properties": {
    "Form": { "type": "string" },
    "Stanza Count": { "type": "integer" },
    "Lines per Stanza": { "type": "integer" },
    "Rhyme Scheme": { "type": "string" }
  },
  "required": [
    "Form",
    "Stanza Count",
    "Lines per Stanza",
    "Rhyme Scheme"
    ],
  "additionalProperties": false
}
```

#### Tool use

Grace also supports generating *sum types* (a.k.a. tagged unions), and you can imagine using this to subsume traditional tool use frameworks.

For example, consider this Grace program:

```haskell
\{ key } ->

let concatSep = import github
      { owner: "Gabriella439"
      , repository: "grace"
      , path: "prelude/text/concatSep.ffg"
      }

let call = fold
      { HttpRequest: \x -> "curl " + x.url
      , ShellCommand: \x -> concatSep " " ([ x.executable ] + x.arguments)
      }

in  map call (prompt{ key, text: "Call some tools" })
```

This doesn't actually *run* any tools (I haven't added any callable tools to my work-in-progress branch yet), but just renders the tool use as a string for now:

```bash
$ grace interpret ./tools.ffg
```
```json
[ "curl https://api.example.com/data", "ls -l -a" ]
```

However, the idea is that you can model a tool as a sum type with one constructor per callable tool, and in the above example the type checker infers that the sum type representing one tool call is:

```haskell
< HttpRequest: { url: Text }
| ShellCommand: { executable: Text, arguments: List Text }
>
```

… so the inferred type of `call` is:

```haskell
call : < HttpRequest: …, ShellCommand: … > -> Text
```

… but since we `map` the `call` function over the output of the `prompt` the type checker infers that the `prompt` needs to generate a `List` of tool calls:

```haskell
prompt{ key, text: "Call some tools" } : List < HttpRequest: …, ShellCommand: … >
```

… and then Grace does some magic under the hood to convert that type to the equivalent JSON schema.

What's particularly neat about this example is that the prompt is so incredibly bare ("Call some tools") because all the information the model needs is present in the schema.

#### Schema-driven prompting

We can explore this idea of using the schema to drive the prompt instead of prose using an example like this:

```haskell
prompt{ key: ./openai.key, text: "Generate some characters for a story" }
  : List
    { "The character's name": Text
    , "The most memorable thing about the character": Text
    , "The character's personal arc": Text
    }
```
```json
[ { "The character's name": "Aveline Thatcher"
  , "The character's personal arc":
      "Aveline starts as a skeptical journalist who doubts the stories of mythical creatures. Over time, she becomes a firm believer, risking her career to uncover the truth and protect these creatures."
  , "The most memorable thing about the character":
      "The intricate tattoo of a phoenix on her forearm that seems to glow when she discovers hidden truths."
  }
, { "The character's name": "Kelan Frost"
  , "The character's personal arc":
      "A former rogue alchemist who turns hero after he inadvertently creates a dangerous substance. Driven by guilt, Kelan seeks redemption by finding an antidote and saving his village."
  , "The most memorable thing about the character":
      "His iridescent blue eyes that seem to see into one's soul, a side effect of his alchemical experiments."
  }
, { "The character's name": "Luciana Blair"
  , "The character's personal arc":
      "Luciana is a reclusive artist who initially fears the world outside her home. After a mysterious vision rejuvenates her, she sets out on a journey of self-discovery, ultimately finding both her voice and courage."
  , "The most memorable thing about the character":
      "Her ability to paint scenes before they happen, which she attributes to the visions she sees in her dreams."
  }
, { "The character's name": "Ezra Hartman"
  , "The character's personal arc":
      "Once a charismatic but self-centered lawyer, Ezra is confronted with a moral crisis that forces him to reevaluate his values. He chooses a path of integrity, becoming an advocate for justice."
  , "The most memorable thing about the character":
      "His perfectly tailored suits that slowly become more casual, symbolizing his transformation and shifting priorities."
  }
, { "The character's name": "Seraphine Mora"
  , "The character's personal arc":
      "Seraphine is a young music prodigy who loses her hearing after an accident. Battling despair, she learns to embrace a new way of 'hearing' music through vibrations and her other senses."
  , "The most memorable thing about the character":
      "The ethereal way she 'dances' with the music, using her entire body to express each note's emotion."
  }
]
```

Grace is a superset of JSON and since JSON supports arbitrary field names so does Grace!  Field names in Grace support arbitrary capitalization, punctuation, and whitespace as long as you quote them, and we can use the field names to "smuggle" the description of each field into the schema.

#### Conclusion

Hopefully this gives you some idea of why I've begun to think of prompt chaining as a programming languages problem.  Type inference is just the beginning and I think it is possible to use a domain-specific programming language not just to simplify the code but to ultimately unlock greater reasoning power.

I'm going to continue to use Grace as a research vehicle for prompt chaining but my LLM-enabled [branch of Grace](https://github.com/Gabriella439/grace/tree/gabriella/llm) (like Grace itself) is not really intended to be used in production and I created it mainly as a proof-of-concept for where I'd like prompt chaining frameworks to go.  If I do end up eventually productionizing this research I will create a proper fork with its own name and the whole works.


================================================
FILE: src/Grace/Aeson.hs
================================================
-- | Utilities for working with the @aeson@ package
module Grace.Aeson where

import Control.Exception.Safe (Exception(..))
import Data.Aeson (FromJSON)
import Data.Text (Text)

import qualified Control.Exception.Safe as Exception
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy as ByteString.Lazy
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding

-- | JSON decoding failed
data JSONDecodingFailed = JSONDecodingFailed
    { message :: String
    , text :: Text
    } deriving stock (Show)

instance Exception JSONDecodingFailed where
    displayException JSONDecodingFailed{ message, text } =
        "Failed to decode output as JSON\n\
        \\n\
        \The following text:\n\
        \\n\
        \" <> Text.unpack text <> "\n\
        \\n\
        \… to decode as JSON.\n\
        \\n\
        \Decoding error message:\n\
        \\n\
        \" <> message

-- | Decode a structured value from JSON-encoded `Text`
decode :: FromJSON a => Text -> IO a
decode text = do
    let bytes = ByteString.Lazy.fromStrict (Encoding.encodeUtf8 text)

    case Aeson.eitherDecode bytes of
        Left message ->
            Exception.throwIO JSONDecodingFailed{ message, text }
        Right a ->
            return a


================================================
FILE: src/Grace/Compat.hs
================================================
{-# LANGUAGE CPP #-}

{-# OPTIONS_GHC -Wno-orphans #-}

-- | An internal utility module for backwards compatibility across GHC releases
-- and different libary versions.
module Grace.Compat where

import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
import Data.Text (Text)

import qualified Data.HashMap.Strict.InsOrd as HashMap
import qualified Data.HashMap.Strict as HashMap.Strict
import qualified Data.List as List

#if MIN_VERSION_aeson(2, 0, 0)
import Data.Aeson.KeyMap (KeyMap)

import qualified Data.Aeson.KeyMap as Aeson
#else
import Data.HashMap.Strict (HashMap)
#endif

#if !MIN_VERSION_containers(0, 6, 6)
import Data.Sequence.Internal (Digit(..), Elem(..), FingerTree(..), Node(..), Seq(..))
import Language.Haskell.TH.Syntax (Lift(..))
#endif

#if MIN_VERSION_aeson(2, 0, 0)
fromAesonMap :: KeyMap v -> InsOrdHashMap Text v
fromAesonMap = HashMap.fromHashMap . Aeson.toHashMapText

toAesonMap :: InsOrdHashMap Text v -> KeyMap v
toAesonMap = Aeson.fromHashMapText . HashMap.toHashMap

sorted :: KeyMap value -> [(Text, value)]
sorted = List.sortOn fst . HashMap.Strict.toList . Aeson.toHashMapText
#else
fromAesonMap :: HashMap Text v -> InsOrdHashMap Text v
fromAesonMap = HashMap.fromHashMap

toAesonMap :: InsOrdHashMap Text v -> HashMap Text v
toAesonMap = HashMap.toHashMap

sorted :: HashMap Text v -> [(Text, v)]
sorted = List.sortOn fst . HashMap.Strict.toList
#endif

#if !MIN_VERSION_containers(0, 6, 6)
deriving stock instance Lift a => Lift (Seq a)
deriving stock instance Lift a => Lift (Digit a)
deriving stock instance Lift a => Lift (Elem a)
deriving stock instance Lift a => Lift (FingerTree a)
deriving stock instance Lift a => Lift (Node a)
#endif


================================================
FILE: src/Grace/Context.hs
================================================
{-# LANGUAGE FlexibleContexts #-}

{-| A `Context` is an ordered list of `Entry`s used as the state for the
    bidirectional type-checking algorithm
-}
module Grace.Context
    ( -- * Types
      Entry(..)
    , Context

      -- * Utilities
    , lookup
    , splitOnUnsolvedType
    , splitOnUnsolvedFields
    , splitOnUnsolvedAlternatives
    , discardUpTo
    , solveType
    , solveRecord
    , solveUnion
    , complete
    ) where

import Data.Text (Text)
import Grace.Domain (Domain)
import Grace.Existential (Existential)
import Grace.Monotype (Monotype)
import Grace.Pretty (Pretty(..), label, operator, punctuation)
import Grace.Type (Type)
import Prelude hiding (lookup)
import Prettyprinter (Doc)
import Prettyprinter.Render.Terminal (AnsiStyle)

import qualified Control.Monad.State.Strict as State
import qualified Grace.Domain as Domain
import qualified Grace.Existential as Existential
import qualified Grace.Monotype as Monotype
import qualified Grace.Type as Type
import qualified Prettyprinter as Pretty

{- $setup

   >>> :set -XOverloadedStrings
   >>> :set -XTypeApplications
   >>> import Grace.Type (Record, Union)
-}

-- | An element of the `Context` list
data Entry s
    = Variable Domain Text
    -- ^ Universally quantified variable
    --
    -- >>> pretty @(Entry ()) (Variable Domain.Type "a")
    -- a: Type
    | Annotation Text (Type s)
    -- ^ A bound variable whose type is known
    --
    -- >>> pretty @(Entry ()) (Annotation "x" "a")
    -- x: a
    | UnsolvedType (Existential Monotype)
    -- ^ A placeholder type variable whose type has not yet been inferred
    --
    -- >>> pretty @(Entry ()) (UnsolvedType 0)
    -- a?
    | UnsolvedFields (Existential Monotype.Record)
    -- ^ A placeholder fields variable whose type has not yet been inferred
    --
    -- >>> pretty @(Entry ()) (UnsolvedFields 0)
    -- a?
    | UnsolvedAlternatives (Existential Monotype.Union)
    -- ^ A placeholder alternatives variable whose type has not yet been
    -- inferred
    --
    -- >>> pretty @(Entry ()) (UnsolvedAlternatives 0)
    -- a?
    | SolvedType (Existential Monotype) Monotype
    -- ^ A placeholder type variable whose type has been (at least partially)
    --   inferred
    --
    -- >>> pretty @(Entry ()) (SolvedType 0 (Monotype.Scalar Monotype.Bool))
    -- a = Bool
    | SolvedFields (Existential Monotype.Record) Monotype.Record
    -- ^ A placeholder fields variable whose type has been (at least partially)
    --   inferred
    --
    -- >>> pretty @(Entry ()) (SolvedFields 0 (Monotype.Fields [("x", "X")] (Monotype.UnsolvedFields 1)))
    -- a = x: X, b?
    | SolvedAlternatives (Existential Monotype.Union) Monotype.Union
    -- ^ A placeholder alternatives variable whose type has been (at least
    --   partially) inferred
    --
    -- >>> pretty @(Entry ()) (SolvedAlternatives 0 (Monotype.Alternatives [("x", "X")] (Monotype.UnsolvedAlternatives 1)))
    -- a = x: X | b?
    | MarkerType (Existential Monotype)
    -- ^ This is used by the bidirectional type-checking algorithm to separate
    --   context entries introduced before and after type-checking a universally
    --   quantified type
    --
    -- >>> pretty @(Entry ()) (MarkerType 0)
    -- ➤ a: Type
    | MarkerFields (Existential Monotype.Record)
    -- ^ This is used by the bidirectional type-checking algorithm to separate
    --   context entries introduced before and after type-checking universally
    --   quantified fields
    --
    -- >>> pretty @(Entry ()) (MarkerFields 0)
    -- ➤ a: Fields
    | MarkerAlternatives (Existential Monotype.Union)
    -- ^ This is used by the bidirectional type-checking algorithm to separate
    --   context entries introduced before and after type-checking universally
    --   quantified alternatives
    --
    -- >>> pretty @(Entry ()) (MarkerAlternatives 0)
    -- ➤ a: Alternatives
    deriving stock (Eq, Show)

instance Pretty (Entry s) where
    pretty = prettyEntry

{-| A `Context` is an ordered list of `Entry`s

    Note that this representation stores the `Context` entries in reverse
    order, meaning that the beginning of the list represents the entries that
    were added last.  For example, this context:

    > •, a : Bool, b, c?, d = c?, ➤e : Type

    … corresponds to this Haskell representation:

    > [ MarkerType 4
    > , SolvedType 3 (Monotype.UnsolvedType 2)
    > , UnsolvedType 2
    > , Variable "b"
    > , Annotation "a" (Monotype.Scalar Monotype.Bool)
    > ]

    The ordering matters because the bidirectional type-checking algorithm
    uses ordering of `Context` entries to determine scope.  Specifically:

    * each `Entry` in the `Context` can only refer to variables preceding it
      within the `Context`

    * the bidirectional type-checking algorithm sometimes discards all entries
      in the context past a certain entry to reflect the end of their
      \"lifetime\"
-}
type Context s = [Entry s]

prettyEntry :: Entry s -> Doc AnsiStyle
prettyEntry (Variable domain a) =
    label (pretty a) <> operator ":" <> " " <> pretty domain
prettyEntry (UnsolvedType a) =
    pretty a <> "?"
prettyEntry (UnsolvedFields p) =
    pretty p <> "?"
prettyEntry (UnsolvedAlternatives p) =
    pretty p <> "?"
prettyEntry (SolvedType a τ) =
    pretty a <> " " <> punctuation "=" <> " " <> pretty τ
prettyEntry (SolvedFields p (Monotype.Fields [] Monotype.EmptyFields)) =
    pretty p <> " " <> punctuation "=" <> " " <> punctuation "•"
prettyEntry (SolvedFields p0 (Monotype.Fields [] (Monotype.UnsolvedFields p1))) =
        pretty p0
    <>  " "
    <>  punctuation "="
    <>  " "
    <>  pretty p1
    <>  "?"
prettyEntry (SolvedFields p0 (Monotype.Fields [] (Monotype.VariableFields p1))) =
        pretty p0
    <>  " "
    <>  punctuation "="
    <>  " "
    <>  label (pretty p1)
prettyEntry (SolvedFields p (Monotype.Fields ((k0, τ0) : kτs) fields)) =
        pretty p
    <>  " = "
    <>  label (pretty k0)
    <>  operator ":"
    <>  " "
    <>  pretty τ0
    <>  foldMap prettyFieldType kτs
    <>  case fields of
            Monotype.EmptyFields ->
                ""
            Monotype.UnsolvedFields p1 ->
                punctuation "," <> " " <> pretty p1 <> "?"
            Monotype.VariableFields p1 ->
                punctuation "," <> " " <> pretty p1
prettyEntry (SolvedAlternatives p (Monotype.Alternatives [] Monotype.EmptyAlternatives)) =
    pretty p <> " " <> punctuation "=" <> " " <> punctuation "•"
prettyEntry (SolvedAlternatives p0 (Monotype.Alternatives [] (Monotype.UnsolvedAlternatives p1))) =
    pretty p0 <> " " <> punctuation "=" <> " " <> pretty p1 <> "?"
prettyEntry (SolvedAlternatives p0 (Monotype.Alternatives [] (Monotype.VariableAlternatives p1))) =
    pretty p0 <> " " <> punctuation "=" <> " " <>  label (pretty p1)
prettyEntry (SolvedAlternatives p0 (Monotype.Alternatives ((k0, τ0) : kτs) fields)) =
        pretty p0
    <>  " "
    <>  punctuation "="
    <>  " "
    <>  prettyAlternativeType (k0, τ0)
    <>  foldMap (\kt -> " " <> punctuation "|" <> " " <> prettyAlternativeType kt) kτs
    <>  case fields of
            Monotype.EmptyAlternatives ->
                ""
            Monotype.UnsolvedAlternatives p1 ->
                " " <> punctuation "|" <> " " <> pretty p1 <> "?"
            Monotype.VariableAlternatives p1 ->
                " " <> punctuation "|" <> " " <> label (pretty p1)
prettyEntry (Annotation x a) = Pretty.group (Pretty.flatAlt long short)
  where
    long =
        Pretty.align
            (   pretty x
            <>  operator ":"
            <>  Pretty.hardline
            <>  "  "
            <>  pretty a
            )

    short = pretty x <> operator ":" <> " " <> pretty a
prettyEntry (MarkerType a) =
    "➤ " <> pretty a <> ": Type"
prettyEntry (MarkerFields a) =
    "➤ " <> pretty a <> ": Fields"
prettyEntry (MarkerAlternatives a) =
    "➤ " <> pretty a <> ": Alternatives"

prettyFieldType :: (Text, Monotype) -> Doc AnsiStyle
prettyFieldType (k, τ) =
    punctuation "," <> " " <> pretty k <> operator ":" <> " " <> pretty τ

prettyAlternativeType :: (Text, Monotype) -> Doc AnsiStyle
prettyAlternativeType (k, τ) =
    pretty k <> operator ":" <> " " <> pretty τ

{-| Substitute a `Type` using the solved entries of a `Context`

    >>> original = Type.UnsolvedType () 0
    >>> pretty @(Type ()) original
    a?

    >>> pretty @(Type ()) (solveType [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] original)
    Bool
-}
solveType :: Context s -> Type s -> Type s
solveType context type_ = foldl snoc type_ context
  where
    snoc t (SolvedType         a τ) = Type.solveType         a τ t
    snoc t (SolvedFields       a r) = Type.solveFields       a r t
    snoc t (SolvedAlternatives a u) = Type.solveAlternatives a u t
    snoc t  _                       = t

{-| Substitute a t`Type.Record` using the solved entries of a `Context`

    >>> original = Type.Fields [("x", Type.Scalar () Monotype.Bool)] (Monotype.UnsolvedFields 0)
    >>> pretty @(Record ()) original
    { x: Bool, a? }

    >>> entry = SolvedFields 0 (Monotype.Fields [] Monotype.EmptyFields)
    >>> pretty entry
    a = •

    >>> pretty @(Record ()) (solveRecord [ entry ] original)
    { x: Bool }
-}
solveRecord :: Context s -> Type.Record s -> Type.Record s
solveRecord context oldFields = newFields
  where
    location =
        error "Grace.Context.solveRecord: Internal error - Missing location field"

    newFields =
        case solveType context Type.Record{ fields = oldFields, location } of
            Type.Record{ fields } -> fields
            _ -> error "Grace.Context.solveRecord: Internal Error - solveType changed a record into something else"

{-| Substitute a t`Type.Union` using the solved entries of a `Context`
    `Context`

    >>> original = Type.Alternatives [("A", Type.Scalar () Monotype.Bool)] (Monotype.UnsolvedAlternatives 0)
    >>> pretty @(Union ()) original
    < A: Bool | a? >

    >>> entry = SolvedAlternatives 0 (Monotype.Alternatives [] Monotype.EmptyAlternatives)
    >>> pretty entry
    a = •

    >>> pretty @(Union ()) (solveUnion [ entry ] original)
    < A: Bool >
-}
solveUnion :: Context s -> Type.Union s -> Type.Union s
solveUnion context oldAlternatives = newAlternatives
  where
    location =
        error "Grace.Context.solveUnion: Internal error - Missing location field"

    newAlternatives =
        case solveType context Type.Union{ alternatives = oldAlternatives, location } of
            Type.Union{ alternatives } -> alternatives
            _ -> error "Grace.Context.solveUnion: Internal error - solveType changed a union into something else"

{-| This function is used at the end of the bidirectional type-checking
    algorithm to complete the inferred type by:

    * Substituting the type with the solved entries in the `Context`

    * Adding universal quantifiers for all unsolved entries in the `Context`

    >>> original = Type.Function () (Type.UnsolvedType () 1) (Type.UnsolvedType () 0)
    >>> pretty @(Type ()) original
    b? -> a?

    >>> pretty @(Type ()) (complete [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ] original)
    forall (a : Type) . a -> Bool
-}
complete :: Context s -> Type s -> Type s
complete context type0 = do
    State.evalState (foldl snoc nil context) 0
  where
    snoc action (SolvedType name solution) = do
        type_ <- action

        return (Type.solveType name solution type_)
    snoc action (SolvedFields name solution) = do
        type_ <- action

        return (Type.solveFields name solution type_)
    snoc action (SolvedAlternatives name solution) = do
        type_ <- action
        return (Type.solveAlternatives name solution type_)
    snoc action (UnsolvedType name₀) = do
        n <- State.get

        State.put $! n + 1

        type_ <- action

        let location = Type.location type_

        let name = Existential.toVariable n

        let nameLocation = location

        let domain = Domain.Type

        let solution = Monotype.VariableType name

        if Type.typeFreeIn name₀ type_
            then do
                return Type.Forall
                    { location
                    , nameLocation
                    , name
                    , domain
                    , type_ = Type.solveType name₀ solution type_
                    }
            else do
                return type_
    snoc action (UnsolvedFields name₀) = do
        n <- State.get

        State.put $! n + 1

        type_ <- action

        let location = Type.location type_

        let name = Existential.toVariable n

        let nameLocation = location

        let domain = Domain.Fields

        let solution = Monotype.Fields [] (Monotype.VariableFields name)

        if Type.fieldsFreeIn name₀ type_
            then do
                return Type.Forall
                    { location
                    , nameLocation
                    , name
                    , domain
                    , type_ = Type.solveFields name₀ solution type_
                    }
            else do
                return type_
    snoc action (UnsolvedAlternatives name₀) = do
        n <- State.get

        State.put $! n + 1

        type_ <- action

        let location = Type.location type_

        let name = Existential.toVariable n

        let nameLocation = location

        let domain = Domain.Alternatives

        let solution = Monotype.Alternatives [] (Monotype.VariableAlternatives name)

        if Type.alternativesFreeIn name₀ type_
            then do
                return Type.Forall
                    { location
                    , nameLocation
                    , name
                    , domain
                    , type_ = Type.solveAlternatives name₀ solution type_
                    }
            else do
                return type_
    snoc action _ = do
        action

    nil = return type0

{-| Split a `Context` into two `Context`s before and after the given
    `UnsolvedType` variable.  Neither `Context` contains the variable

    Returns `Nothing` if no such `UnsolvedType` variable is present within the
    `Context`

    >>> splitOnUnsolvedType 1 [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ]
    Just ([],[SolvedType 0 (Scalar Bool)])
    >>> splitOnUnsolvedType 0 [ UnsolvedType 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ]
    Nothing
-}
splitOnUnsolvedType
    :: Existential Monotype
    -- ^ `UnsolvedType` variable to split on
    -> Context s
    -> Maybe (Context s, Context s)
splitOnUnsolvedType a0 (UnsolvedType a1 : entries)
    | a0 == a1 = return ([], entries)
splitOnUnsolvedType a (entry : entries) = do
    (prefix, suffix) <- splitOnUnsolvedType a entries
    return (entry : prefix, suffix)
splitOnUnsolvedType _ [] = Nothing

{-| Split a `Context` into two `Context`s before and after the given
    `UnsolvedFields` variable.  Neither `Context` contains the variable

    Returns `Nothing` if no such `UnsolvedFields` variable is present within the
    `Context`

    >>> splitOnUnsolvedFields 1 [ UnsolvedFields 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ]
    Just ([],[SolvedType 0 (Scalar Bool)])
    >>> splitOnUnsolvedFields 0 [ UnsolvedFields 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ]
    Nothing
-}
splitOnUnsolvedFields
    :: Existential Monotype.Record
    -- ^ `UnsolvedFields` variable to split on
    -> Context s
    -> Maybe (Context s, Context s)
splitOnUnsolvedFields p0 (UnsolvedFields p1 : entries)
    | p0 == p1 = return ([], entries)
splitOnUnsolvedFields p (entry : entries) = do
    (prefix, suffix) <- splitOnUnsolvedFields p entries
    return (entry : prefix, suffix)
splitOnUnsolvedFields _ [] = Nothing

{-| Split a `Context` into two `Context`s before and after the given
    `UnsolvedAlternatives` variable.  Neither `Context` contains the variable

    Returns `Nothing` if no such `UnsolvedAlternatives` variable is present
    within the `Context`

    >>> splitOnUnsolvedAlternatives 1 [ UnsolvedAlternatives 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ]
    Just ([],[SolvedType 0 (Scalar Bool)])
    >>> splitOnUnsolvedAlternatives 0 [ UnsolvedAlternatives 1, SolvedType 0 (Monotype.Scalar Monotype.Bool) ]
    Nothing
-}
splitOnUnsolvedAlternatives
    :: Existential Monotype.Union
    -- ^ `UnsolvedAlternatives` variable to split on
    -> Context s
    -> Maybe (Context s, Context s)
splitOnUnsolvedAlternatives p0 (UnsolvedAlternatives p1 : entries)
    | p0 == p1 = return ([], entries)
splitOnUnsolvedAlternatives p (entry : entries) = do
    (prefix, suffix) <- splitOnUnsolvedAlternatives p entries
    return (entry : prefix, suffix)
splitOnUnsolvedAlternatives _ [] = Nothing

{-| Retrieve a variable's annotated type from a `Context`, given the variable's
    label and index

    >>> lookup "x" [ Annotation "x" (Type.Scalar () Monotype.Bool), Annotation "y" (Type.Scalar () Monotype.Natural) ]
    Just (Scalar {location = (), scalar = Bool})
-}
lookup
    :: Text
    -- ^ Variable label
    -> Context s
    -> Maybe (Type s)
lookup _                      []  = Nothing
lookup x0 (Annotation x1 _A : _Γ) = if x0 == x1 then Just _A else lookup x0 _Γ
lookup x  (_                : _Γ) = lookup x _Γ

{-| Discard all entries from a `Context` up to and including the given `Entry`

    >>> discardUpTo (MarkerType 1) [ UnsolvedType 1, MarkerType 1, UnsolvedType 0 ]
    [UnsolvedType 0]
-}
discardUpTo :: Eq s => Entry s -> Context s -> Context s
discardUpTo entry0 (entry1 : _Γ)
    | entry0 == entry1 = _Γ
    | otherwise = discardUpTo entry0 _Γ
discardUpTo _ [] = []


================================================
FILE: src/Grace/Decode.hs
================================================
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE OverloadedLists     #-}

{-| Use this module to decode Grace expressions into Haskell expressions

    Example usage:

    >>> decode (Value.Scalar Unknown (Syntax.Bool True)) :: Either DecodingError Bool
    Right True
-}
module Grace.Decode
    ( -- * Classes
      FromGrace(..)
    , ToGraceType(..)
    , GenericFromGrace(..)
    , GenericToGraceType(..)

      -- * Types
    , Key(..)

      -- * Exceptions
    , DecodingError(..)
    ) where

import Control.Exception.Safe (Exception)
import Control.Monad.State (State)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Scientific (Scientific)
import Data.Sequence (Seq)
import Data.Text (Text)
import Data.Vector (Vector)
import Data.Void (Void)
import Data.Word (Word8, Word16, Word32, Word64)
import Grace.Location (Location(..))
import Grace.Type (Type)
import Grace.Value (Value(..))
import Numeric.Natural (Natural)

import qualified Data.Text as Text

import GHC.Generics
    ( Generic(..)
    , C
    , Constructor
    , D
    , K1(..)
    , M1(..)
    , Rep
    , S
    , Selector
    , U1(..)
    , V1
    , (:+:)(..)
    , (:*:)(..)
    )
import Grace.Marshal
    (Key(..), GenericToGraceType(..), ToGraceType(..), selector)

import qualified Control.Exception.Safe as Exception
import qualified Control.Monad.State as State
import qualified Data.Aeson as Aeson
import qualified Data.Foldable as Foldable
import qualified Data.HashMap.Strict.InsOrd as HashMap
import qualified Data.Scientific as Scientific
import qualified Data.Text.Lazy as Text.Lazy
import qualified Data.Vector as Vector
import qualified GHC.Generics as Generics
import qualified Grace.Pretty as Grace.Pretty
import qualified Grace.Syntax as Syntax
import qualified Grace.Value as Value
import qualified Prettyprinter as Pretty

-- | Convert a Grace expression to a Haskell expression
class ToGraceType a => FromGrace a where
    decode :: Value Location -> Either DecodingError a
    default decode
        :: (Generic a, GenericFromGrace (Rep a))
        => Value Location -> Either DecodingError a
    decode = fmap (fmap to) (State.evalState genericDecode 0)

    -- | This is used for decoding record fields, which might not be present
    decodeMaybe :: Maybe (Value Location) -> Either DecodingError a
    decodeMaybe (Just value) = decode value
    decodeMaybe Nothing = Left TypeError
        { expectedType = expected @(Maybe a)
        , value = Value.Scalar Unknown Syntax.Null
        }

instance FromGrace Void
instance FromGrace ()
instance (FromGrace a, FromGrace b) => FromGrace (a, b)
instance (FromGrace a, FromGrace b) => FromGrace (Either a b)

instance FromGrace Bool where
    decode (Value.Scalar _ (Syntax.Bool bool)) = return bool
    decode value = Left TypeError{ expectedType = expected @Bool, value }

instance FromGrace Natural where
    decode (Value.Scalar _ (Syntax.Natural natural)) =
        return natural
    decode value =
        Left TypeError{ expectedType = expected @Natural, value }

decodeIntegral
    ::  forall a b
    .   (FromGrace a, Integral a, Integral b, Bounded b)
    =>  Value Location -> Either DecodingError b
decodeIntegral value = do
    integral <- decode @a value

    if      fromIntegral (minBound @b) <= integral
        &&  integral <= fromIntegral (maxBound @b)
        then return (fromIntegral @a @b integral)
        else Left RangeError

instance FromGrace Word where
    decode = decodeIntegral @Natural @Word

instance FromGrace Word8 where
    decode = decodeIntegral @Natural @Word8

instance FromGrace Word16 where
    decode = decodeIntegral @Natural @Word16

instance FromGrace Word32 where
    decode = decodeIntegral @Natural @Word32

instance FromGrace Word64 where
    decode = decodeIntegral @Natural @Word64

instance FromGrace Integer where
    decode (Value.Scalar _ (Syntax.Natural natural)) =
        return (fromIntegral natural)
    decode (Value.Scalar _ (Syntax.Integer integer)) =
        return integer
    decode value = Left TypeError
        { expectedType = expected @Integer
        , value = value
        }

instance FromGrace Int where
    decode = decodeIntegral @Integer @Int

instance FromGrace Int8 where
    decode = decodeIntegral @Integer @Int8

instance FromGrace Int16 where
    decode = decodeIntegral @Integer @Int16

instance FromGrace Int32 where
    decode = decodeIntegral @Integer @Int32

instance FromGrace Int64 where
    decode = decodeIntegral @Integer @Int64

instance FromGrace Scientific where
    decode (Value.Scalar _ (Syntax.Natural natural)) =
        return (fromIntegral natural)
    decode (Value.Scalar _ (Syntax.Integer integer)) =
        return (fromInteger integer)
    decode (Value.Scalar _ (Syntax.Real scientific)) =
        return scientific
    decode value =
        Left TypeError{ expectedType = expected @Scientific, value }

decodeRealFloat :: RealFloat a => Value Location -> Either DecodingError a
decodeRealFloat value = do
    scientific <- decode value

    case Scientific.toBoundedRealFloat scientific of
        Left _ -> Left RangeError
        Right realFloat -> return realFloat

instance FromGrace Double where
    decode = decodeRealFloat

instance FromGrace Float where
    decode = decodeRealFloat

instance FromGrace Text where
    decode (Value.Text _ text) = return text
    decode value = Left TypeError{ expectedType = expected @Text, value }

instance FromGrace Text.Lazy.Text where
    decode = fmap (fmap Text.Lazy.fromStrict) decode

instance {-# OVERLAPPING #-} FromGrace [Char] where
    decode = fmap (fmap Text.unpack) decode

instance FromGrace Key where
    decode (Value.Scalar _ (Syntax.Key text)) = return Key{ text }
    decode value = Left TypeError{ expectedType = expected @Key, value }

instance FromGrace Aeson.Value where
    decode value = case Value.toJSON value of
        Nothing ->
            Left TypeError{ expectedType = expected @Aeson.Value, value }
        Just json ->
            return json

instance FromGrace a => FromGrace (Seq a) where
    decode (Value.List _ seq_) = traverse decode seq_
    decode value = Left TypeError{ expectedType = expected @(Seq a), value }

instance FromGrace a => FromGrace [a] where
    decode = fmap (fmap Foldable.toList) (decode @(Seq a))

instance FromGrace a => FromGrace (Vector a) where
    decode = fmap (fmap Vector.fromList) decode

instance FromGrace a => FromGrace (Maybe a) where
    decode (Value.Scalar _ Syntax.Null) = do
        return Nothing
    decode (Value.Application _ (Value.Builtin _ Syntax.Some) value) = do
        a <- decode value
        return (Just a)
    decode value = do
        Left TypeError{ expectedType = expected @(Maybe a), value }

    decodeMaybe Nothing = do
        return Nothing
    decodeMaybe (Just value) = do
        decode value

{-| This is the underlying class that powers the `FromGrace` class's support for
    automatically deriving a `Generic` implementation
-}
class GenericToGraceType f => GenericFromGrace f where
    genericDecode :: State Int (Value Location -> Either DecodingError (f a))

instance GenericFromGrace V1 where
    genericDecode = do
        let expectedType = State.evalState (genericExpected @V1) 0

        let decode_ value = Left TypeError{ expectedType, value }

        return decode_

instance GenericFromGrace U1 where
    genericDecode = do
        let expectedType = State.evalState (genericExpected @U1) 0

        let decode_ (Value.Record _ []) = return U1
            decode_ value = Left TypeError{ expectedType, value }

        return decode_

instance GenericFromGrace f => GenericFromGrace (M1 D d f) where
    genericDecode = fmap (fmap (fmap M1)) genericDecode

instance GenericFromGrace f => GenericFromGrace (M1 C d f) where
    genericDecode = fmap (fmap (fmap M1)) genericDecode

instance (Selector s, FromGrace a) => GenericFromGrace (M1 S s (K1 i a)) where
    genericDecode = do
        let m1 :: M1 S s (K1 i a) r
            m1 = undefined

        name <- selector m1

        let decode_ value
                | Generics.selName m1 == "" =
                    fmap (M1 . K1) (decode value)
                | otherwise = case value of
                    Value.Record _ fieldValues ->
                        fmap (M1 . K1) (decodeMaybe (fmap snd (HashMap.lookup name fieldValues)))
                    _ -> do
                        let expectedType =
                                State.evalState (genericExpected @(M1 S s (K1 i a))) 0

                        Left TypeError{ expectedType, value }

        return decode_

instance (Selector s₀, Selector s₁, FromGrace a₀, FromGrace a₁) => GenericFromGrace (M1 S s₀ (K1 i₀ a₀) :*: M1 S s₁ (K1 i₁ a₁)) where
    genericDecode = do
        name₀ <- selector (undefined :: M1 S s₀ (K1 i₀ a₀) r)
        name₁ <- selector (undefined :: M1 S s₁ (K1 i₁ a₁) r)

        let decode_ (Value.Record _ fieldValues) = do
                expression₀ <- decodeMaybe (fmap snd (HashMap.lookup name₀ fieldValues))
                expression₁ <- decodeMaybe (fmap snd (HashMap.lookup name₁ fieldValues))

                return (M1 (K1 expression₀) :*: M1 (K1 expression₁))
            decode_ value = do
                let expectedType =
                        State.evalState (genericExpected @(M1 S s₀ (K1 i₀ a₀) :*: M1 S s₁ (K1 i₁ a₁))) 0

                Left TypeError{ expectedType, value }

        return decode_

instance (Selector s, GenericFromGrace (f₀ :*: f₁), FromGrace a) => GenericFromGrace ((f₀ :*: f₁) :*: M1 S s (K1 i a)) where
    genericDecode = do
        decode₀ <- genericDecode

        name <- selector (undefined :: M1 S s (K1 i a) r)

        let decode_ value₀@(Value.Record _ fieldValues) = do
                expression₀ <- decode₀ value₀
                expression₁ <- decodeMaybe (fmap snd (HashMap.lookup name fieldValues))

                return (expression₀ :*: M1 (K1 expression₁))

            decode_ value = do
                let expectedType =
                        State.evalState (genericExpected @((f₀ :*: f₁) :*: M1 S s (K1 i a))) 0

                Left TypeError{ expectedType, value }

        return decode_

instance (Selector s, FromGrace a, GenericFromGrace (f₀ :*: f₁)) => GenericFromGrace (M1 S s (K1 i a) :*: (f₀ :*: f₁)) where
    genericDecode = do
        name <- selector (undefined :: M1 S s (K1 i a) r)

        decode₁ <- genericDecode

        let decode_ value₁@(Value.Record _ fieldValues) = do
                expression₀ <- decodeMaybe (fmap snd (HashMap.lookup name fieldValues))
                expression₁ <- decode₁ value₁

                return (M1 (K1 expression₀) :*: expression₁)

            decode_ value = do
                let expectedType =
                        State.evalState (genericExpected @(M1 S s (K1 i a) :*: (f₀ :*: f₁))) 0

                Left TypeError{ expectedType, value }

        return decode_

instance (GenericFromGrace (f₀ :*: f₁), GenericFromGrace (f₂ :*: f₃)) => GenericFromGrace ((f₀ :*: f₁) :*: (f₂ :*: f₃)) where
    genericDecode = do
        decode₀ <- genericDecode
        decode₁ <- genericDecode

        let decode_ value = do
                expression₀ <- decode₀ value
                expression₁ <- decode₁ value

                return (expression₀ :*: expression₁)

        return decode_

instance (Constructor c₀, Constructor c₁, GenericFromGrace f₀, GenericFromGrace f₁) => GenericFromGrace (M1 C c₀ f₀ :+: M1 C c₁ f₁) where
    genericDecode = do
        let name₀ = Text.pack (Generics.conName (undefined :: M1 C c₀ f₀ r))
        let name₁ = Text.pack (Generics.conName (undefined :: M1 C c₁ f₁ r))

        let decode₀ = State.evalState genericDecode 0
        let decode₁ = State.evalState genericDecode 0

        let decode_ v@(Value.Alternative _ name value)
                | name == name₀ = fmap (L1 . M1) (decode₀ value)
                | name == name₁ = fmap (R1 . M1) (decode₁ value)
                | otherwise = do
                    let expectedType =
                            State.evalState (genericExpected @(M1 C c₀ f₀ :+: M1 C c₁ f₁)) 0

                    Left TypeError{ expectedType, value = v }
            decode_ v = do
                let expectedType =
                            State.evalState (genericExpected @(M1 C c₀ f₀ :+: M1 C c₁ f₁)) 0

                Left TypeError{ expectedType, value = v }

        return decode_

instance (Constructor c, GenericFromGrace f₀, GenericFromGrace (f₁ :+: f₂)) => GenericFromGrace (M1 C c f₀ :+: (f₁ :+: f₂)) where
    genericDecode = do
        let name₀ = Text.pack (Generics.conName (undefined :: M1 C c f r))

        let decode₀ = State.evalState genericDecode 0
        let decode₁ = State.evalState genericDecode 0

        let decode_ (Value.Alternative _ name value₀)
                | name == name₀ = fmap (L1 . M1) (decode₀ value₀)
            decode_ value₁ = fmap R1 (decode₁ value₁)

        return decode_

instance (Constructor c, GenericFromGrace (f₀ :+: f₁), GenericFromGrace f₂) => GenericFromGrace ((f₀ :+: f₁) :+: M1 C c f₂) where
    genericDecode = do
        let name₁ = Text.pack (Generics.conName (undefined :: M1 C c f r))

        let decode₀ = State.evalState genericDecode 0
        let decode₁ = State.evalState genericDecode 0

        let decode_ (Value.Alternative _ name value₁)
                | name == name₁ = fmap (R1 . M1) (decode₁ value₁)
            decode_ value₀ = fmap L1 (decode₀ value₀)

        return decode_

instance (GenericFromGrace (f₀ :+: f₁), GenericFromGrace (f₂ :+: f₃)) => GenericFromGrace ((f₀ :+: f₁) :+: (f₂ :+: f₃)) where
    genericDecode = do
        let decode₀ = State.evalState genericDecode 0
        let decode₁ = State.evalState genericDecode 0

        let decode_ value = do
                let Right a <|> _ = Right a

                    _ <|> Right a = Right a

                    Left TypeError{ } <|> Left TypeError{ } = do
                        let expectedType =
                                State.evalState (genericExpected @((f₀ :+: f₁) :+: (f₂ :+: f₃))) 0

                        Left TypeError{ expectedType, value }

                    _ <|> _ = Left RangeError

                fmap L1 (decode₀ value) <|> fmap R1 (decode₁ value)

        return decode_

-- | Result of decoding
data DecodingError
    = TypeError{ expectedType :: Type (), value :: Value Location }
    -- ^ The input Grace expression has the wrong type
    | RangeError
    -- ^ The input Grace expression is out of bounds for the target Haskell type
    deriving stock (Eq, Show)

instance Exception DecodingError where
    displayException TypeError{ expectedType, value } =
        "Failed to decode Grace value into Haskell because of a type mismatch\n\
        \\n\
        \The following value:\n\
        \\n\
        \" <> Text.unpack (Grace.Pretty.toSmart ("  " <> Pretty.align (Grace.Pretty.pretty value))) <> "\n\
        \\n\
        \… could not be decoded into Haskell because it does not match this type:\n\
        \\n\
        \" <> Text.unpack (Grace.Pretty.toSmart ("  " <> Pretty.align (Grace.Pretty.pretty expectedType)))
    displayException RangeError =
        "Failed to decode value because the decoded result was out of bounds"


================================================
FILE: src/Grace/Domain.hs
================================================
{-| This module exists primarily to avoid a name clash with constructors of the
    same name in the "Grace.Type" module
-}
module Grace.Domain
    ( -- * Domain
      Domain(..)
    ) where

import GHC.Generics (Generic)
import Grace.Pretty (Pretty(..), builtin)
import Language.Haskell.TH.Syntax (Lift)

-- | The domain over which a @forall@ is quantified
data Domain
    = Type
    -- ^ @forall (a : Type) . …@
    | Fields
    -- ^ @forall (a : Fields) . …@
    | Alternatives
    -- ^ @forall (a : Alternatives) . …@
    deriving stock (Eq, Generic, Lift, Show)

instance Pretty Domain where
    pretty Type         = builtin "Type"
    pretty Fields       = builtin "Fields"
    pretty Alternatives = builtin "Alternatives"


================================================
FILE: src/Grace/Encode.hs
================================================
{-| Use this module to encode Haskell expressions as Grace expressions

    Example usage:

    >>> encode True
    Scalar () (Bool True)
-}
module Grace.Encode
    ( -- * Classes
      ToGrace(..)
    , GenericToGrace(..)
    , ToGraceType(..)
    , GenericToGraceType(..)

      -- * Types
    , Key(..)
    ) where

import Control.Monad.State (State)
import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Scientific (Scientific)
import Data.Sequence (Seq)
import Data.Text (Text)
import Data.Vector (Vector)
import Data.Void (Void)
import Data.Word (Word8, Word16, Word32, Word64)
import Grace.Marshal (GenericToGraceType(..), Key(..), ToGraceType(..))
import Grace.Value (Value(..))
import Numeric.Natural (Natural)

import GHC.Generics
    ( Generic(..)
    , C
    , Constructor
    , D
    , K1(..)
    , M1(..)
    , Rep
    , S
    , Selector
    , U1(..)
    , V1
    , (:+:)(..)
    , (:*:)(..)
    )

import qualified Control.Monad.State as State
import qualified Data.Aeson as Aeson
import qualified Data.Scientific as Scientific
import qualified Data.Sequence as Seq
import qualified Data.HashMap.Strict.InsOrd as HashMap
import qualified Data.Text as Text
import qualified Data.Text.Lazy as Text.Lazy
import qualified Data.Vector as Vector
import qualified GHC.Generics as Generics
import qualified Grace.Marshal as Marshal
import qualified Grace.Syntax as Syntax
import qualified Grace.Value as Value

-- | Convert a Haskell expression to a Grace expression
class ToGraceType a => ToGrace a where
    encode :: a -> Value ()

    default encode :: (Generic a, GenericToGrace (Rep a)) => a -> Value ()
    encode = State.evalState genericEncode 0 . from

instance ToGrace Void
instance ToGrace ()
instance (ToGrace a, ToGrace b) => ToGrace (a, b)
instance (ToGrace a, ToGrace b) => ToGrace (Either a b)

instance ToGrace Bool where
    encode bool = Value.Scalar () (Syntax.Bool bool)

instance ToGrace Natural where
    encode natural = Value.Scalar () (Syntax.Natural natural)

instance ToGrace Word where
    encode = encode @Natural . fromIntegral

instance ToGrace Word8 where
    encode = encode @Natural . fromIntegral

instance ToGrace Word16 where
    encode = encode @Natural . fromIntegral

instance ToGrace Word32 where
    encode = encode @Natural . fromIntegral

instance ToGrace Word64 where
    encode = encode @Natural . fromIntegral

instance ToGrace Integer where
    encode integer = Value.Scalar () (Syntax.Integer integer)

instance ToGrace Int where
    encode = encode @Integer . fromIntegral

instance ToGrace Int8 where
    encode = encode @Integer . fromIntegral

instance ToGrace Int16 where
    encode = encode @Integer . fromIntegral

instance ToGrace Int32 where
    encode = encode @Integer . fromIntegral

instance ToGrace Int64 where
    encode = encode @Integer . fromIntegral

instance ToGrace Scientific where
    encode scientific = Value.Scalar () (Syntax.Real scientific)

instance ToGrace Float where
    encode = encode @Scientific . Scientific.fromFloatDigits

instance ToGrace Double where
    encode = encode @Scientific . Scientific.fromFloatDigits

instance ToGrace Text where
    encode text = Value.Text () text

instance ToGrace Text.Lazy.Text where
    encode = encode . Text.Lazy.toStrict

instance {-# OVERLAPPING #-} ToGrace [Char] where
    encode = encode . Text.pack

instance ToGrace Key where
    encode Key{ text } = Value.Scalar () (Syntax.Key text)

instance ToGrace Aeson.Value where
    encode = Value.fromJSON

instance ToGrace a => ToGrace (Seq a) where
    encode list = Value.List () (fmap encode list)

instance ToGrace a => ToGrace [a] where
    encode = encode . Seq.fromList

instance ToGrace a => ToGrace (Vector a) where
    encode = encode . Vector.toList

instance ToGrace a => ToGrace (Maybe a) where
    encode (Just a) =
        Value.Application () (Value.Builtin () Syntax.Some) (encode a)
    encode Nothing = Value.Scalar () Syntax.Null

{-| This is the underlying class that powers the `ToGrace` class's support for
    automatically deriving a `Generic` implementation
-}
class GenericToGrace f where
    genericEncode :: State Int (f a -> Value ())

instance GenericToGrace V1 where
    genericEncode = do
        -- EmptyCase does not work here and produces a non-exhaustive pattern
        -- match warning
        let encode_ _ = error "Grace.Encode.genericEncode: V1 inhabited"

        return encode_

instance GenericToGrace U1 where
    genericEncode = do
        let encode_ U1 = Value.Record () mempty

        return encode_

instance GenericToGrace f => GenericToGrace (M1 D d f) where
    genericEncode = fmap (. unM1) genericEncode

instance GenericToGrace f => GenericToGrace (M1 C c f) where
    genericEncode = fmap (. unM1) genericEncode

instance (Selector s, ToGrace a) => GenericToGrace (M1 S s (K1 i a)) where
    genericEncode = do
        let m1 :: M1 S s (K1 i a) r
            m1 = undefined

        name <- Marshal.selector m1

        let encode_ (M1 (K1 a))
                | Generics.selName m1 == "" =
                    encode a
                | otherwise =
                    Value.Record () (HashMap.singleton name ((), encode a))

        return encode_

instance (Selector s₀, Selector s₁, ToGrace a₀, ToGrace a₁) => GenericToGrace (M1 S s₀ (K1 i₀ a₀) :*: M1 S s₁ (K1 i₁ a₁)) where
    genericEncode = do
        name₀ <- Marshal.selector (undefined :: M1 S s₀ (K1 i₀ a₀) r)
        name₁ <- Marshal.selector (undefined :: M1 S s₁ (K1 i₁ a₁) r)

        let encode_ (M1 (K1 a₀) :*: M1 (K1 a₁)) =
                Value.Record
                    ()
                    (HashMap.fromList
                        [ (name₀, ((), encode a₀)), (name₁, ((), encode a₁)) ]
                    )

        return encode_

instance (Selector s, GenericToGrace (f₀ :*: f₁), ToGrace a) => GenericToGrace ((f₀ :*: f₁) :*: M1 S s (K1 i a)) where
    genericEncode = do
        encode₀ <- genericEncode

        name <- Marshal.selector (undefined :: M1 S s (K1 i a) r)

        let encode_ (f :*: M1 (K1 a)) = Value.Record
                ()
                ( HashMap.insert name ((), encode a)
                    (unsafeExpectRecordLiteral (encode₀ f))
                )

        return encode_

instance (Selector s, ToGrace a, GenericToGrace (f₀ :*: f₁)) => GenericToGrace (M1 S s (K1 i a) :*: (f₀ :*: f₁)) where
    genericEncode = do
        name <- Marshal.selector (undefined :: M1 S s (K1 i a) r)

        encode₁ <- genericEncode

        let encode_ (M1 (K1 a) :*: f) = Value.Record
                ()
                (HashMap.insert name ((), encode a)
                  (unsafeExpectRecordLiteral (encode₁ f))
                )

        return encode_

instance (GenericToGrace (f₀ :*: f₁), GenericToGrace (f₂ :*: f₃)) => GenericToGrace ((f₀ :*: f₁) :*: (f₂ :*: f₃)) where
    genericEncode = do
        encode₀ <- genericEncode
        encode₁ <- genericEncode

        let encode_ (f₀ :*: f₁) = Value.Record
                ()
                (   unsafeExpectRecordLiteral (encode₀ f₀)
                <>  unsafeExpectRecordLiteral (encode₁ f₁)
                )

        return encode_

instance (Constructor c₀, Constructor c₁, GenericToGrace f₀, GenericToGrace f₁) => GenericToGrace (M1 C c₀ f₀ :+: M1 C c₁ f₁) where
    genericEncode = do
        let name₀ = Text.pack (Generics.conName (undefined :: M1 i c₀ f₀ r))
        let name₁ = Text.pack (Generics.conName (undefined :: M1 i c₁ f₁ r))

        let encode₀ = State.evalState genericEncode 0
        let encode₁ = State.evalState genericEncode 0

        let encode_ (L1 (M1 f)) = Value.Alternative () name₀ (encode₀ f)
            encode_ (R1 (M1 f)) = Value.Alternative () name₁ (encode₁ f)

        return encode_

instance (Constructor c, GenericToGrace f₀, GenericToGrace (f₁ :+: f₂)) => GenericToGrace (M1 C c f₀ :+: (f₁ :+: f₂)) where
    genericEncode = do
        let name = Text.pack (Generics.conName (undefined :: M1 C c f₀ r))

        let encode₀ = State.evalState genericEncode 0
        let encode₁ = State.evalState genericEncode 0

        let encode_ (L1 (M1 f)) = Value.Alternative () name (encode₀ f)
            encode_ (R1     f ) = encode₁ f

        return encode_

instance (Constructor c, GenericToGrace (f₀ :+: f₁), GenericToGrace f₂) => GenericToGrace ((f₀ :+: f₁) :+: M1 C c f₂) where
    genericEncode = do
        let name = Text.pack (Generics.conName (undefined :: M1 C c f₂ r))

        let encode₀ = State.evalState genericEncode 0
        let encode₁ = State.evalState genericEncode 0

        let encode_ (L1     f ) = encode₀ f
            encode_ (R1 (M1 f)) = Value.Alternative () name (encode₁ f)

        return encode_

instance (GenericToGrace (f₀ :+: f₁), GenericToGrace (f₂ :+: f₃)) => GenericToGrace ((f₀ :+: f₁) :+: (f₂ :+: f₃)) where
    genericEncode = do
        let encode₀ = State.evalState genericEncode 0
        let encode₁ = State.evalState genericEncode 0

        let encode_ (L1 f) = encode₀ f
            encode_ (R1 f) = encode₁ f

        return encode_

unsafeExpectRecordLiteral
    :: Value location -> InsOrdHashMap Text (location, Value location)
unsafeExpectRecordLiteral (Value.Record _ fieldValues) =
    fieldValues
unsafeExpectRecordLiteral _ =
    error "Grace.Encode.unsafeExpectRecordLiteral: not a record"


================================================
FILE: src/Grace/Existential.hs
================================================
{-| This module provides a newtype wrapper for existential variables used in the
    type-checking algorithm.

    We don't use naked `Int`s since (empirically) conflating the various
    existentials is a common source of type-checking errors.
-}
module Grace.Existential
    ( -- * Types
      Existential
      -- * Utilities
    , toVariable
    ) where

import Data.Text (Text)
import Grace.Pretty (Pretty(..), label)
import Language.Haskell.TH.Syntax (Lift)

import qualified Data.Char as Char
import qualified Data.Text as Text

{-| An existential variable

    The type variable is used to track what type of existential variable we're
    using, which will be one of these three types:

    * @`Existential` "Grace.Monotype".Monotype@ - An existential type
    * @`Existential` "Grace.Monotype".Record@ - An existential fields variable
    * @`Existential` "Grace.Monotype".Union@ - An existential alternatives
      variable
-}
newtype Existential a = UnsafeExistential Int
    deriving stock Lift
    deriving newtype (Eq, Num, Show)

instance Pretty (Existential a) where
    pretty x = label (pretty (toVariable x))

{-| Convert an existential variable to a user-friendly `Text`
    representation

    >>> toVariable 0
    "a"
    >>> toVariable 1
    "b"
    >>> toVariable 26
    "a0"
-}
toVariable :: Existential a -> Text
toVariable (UnsafeExistential n) = Text.cons prefix suffix
  where
    (q, r) = n `quotRem` 26

    prefix = Char.chr (Char.ord 'a' + r)

    suffix = if q == 0 then "" else Text.pack (show (q - 1))


================================================
FILE: src/Grace/GitHub.hs
================================================
-- | Utilities for interacting with GitHub
module Grace.GitHub
    ( -- * GitHub requests
      github
    , GitHub(..)
    ) where

import Data.Aeson (FromJSON)
import Data.Text (Text)
import GHC.Generics (Generic)
import Grace.Decode (FromGrace(..), Key(..), ToGraceType)
import Grace.Encode (ToGrace)
import Grace.HTTP.Type (Header(..), HTTP(..), Parameter(..))

import qualified Data.Text as Text
import qualified Grace.Aeson
import qualified Grace.HTTP as HTTP

-- | An HTTP request to GitHub
data GitHub = GitHub
    { key :: Maybe Key
    , reference :: Maybe Text
    , owner :: Text
    , repository :: Text
    , path :: Text
    } deriving stock (Generic)
      deriving anyclass (FromGrace, ToGrace, ToGraceType)

-- | Response from GitHub @\/repos/${owner}\/${repo}\/contents\/${path}@ API
data Contents = Contents{ download_url :: Text }
    deriving stock (Generic)
    deriving anyclass (FromJSON)

{-| Get the download URL of a file on GitHub

    This is an ergonomic convenience for the user for the exceedingly common use
    case of fetching code in version control from GitHub (and also powers
    trygrace.dev's `/github/${owner}/${repository}/${path}` short-links.
-}
github :: GitHub -> IO Text
github GitHub{ key, owner, repository, reference, path } = do
    let authorization = case key of
            Nothing ->
                [ ]
            Just (Key k) ->
                [ Header{ header = "Authorization", value = "Bearer " <> Text.strip k } ]

    let headers = Just
            (   [ Header{ header = "X-GitHub-Api-Version", value = "2022-11-28" }
                , Header{ header = "User-Agent", value = "Grace" }
                ]
            <>  authorization
            )

    let parameters = do
            r <- reference

            return [ Parameter{ parameter = "ref", value = Just r } ]

    contentsResponse <- HTTP.http False GET
        { url = "https://api.github.com/repos/" <> owner <> "/" <> repository <> "/contents/" <> path
        , headers
        , parameters
        }

    Contents{ download_url } <- Grace.Aeson.decode contentsResponse

    return download_url


================================================
FILE: src/Grace/HTTP/Type.hs
================================================
-- | This module contains types shared between the GHC and GHCJS implementations
module Grace.HTTP.Type where

import Data.Aeson (Value)
import Data.Text (Text)
import GHC.Generics (Generic)
import Grace.Decode (FromGrace, ToGraceType)
import Grace.Encode (ToGrace)

import qualified Control.Exception.Safe as Exception
import qualified Data.Binary.Builder as Builder
import qualified Data.ByteString.Lazy as ByteString.Lazy
import qualified Data.CaseInsensitive as CaseInsensitive
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding
import qualified Data.Time.Clock.POSIX as Time
import qualified Network.HTTP.Types as HTTP.Types

-- | An HTTP header
data Header = Header{ header :: Text, value :: Text }
    deriving stock (Generic)
    deriving anyclass (FromGrace, ToGrace, ToGraceType)

-- | A query parameter
data Parameter = Parameter{ parameter :: Text, value :: Maybe Text }
    deriving stock (Generic)
    deriving anyclass (FromGrace, ToGrace, ToGraceType)

-- | An HTTP request
data HTTP
    = GET
        { url :: Text
        , headers :: Maybe [Header]
        , parameters :: Maybe [Parameter]
        }
    | POST
        { url :: Text
        , headers :: Maybe [Header]
        , request :: Maybe Value
        }
    deriving stock (Generic)
    deriving anyclass (FromGrace, ToGrace, ToGraceType)

completeHeaders :: Bool -> Bool -> Maybe [Header] -> [HTTP.Types.Header]
completeHeaders import_ body headers = do
    Header{ header, value } <- requiredHeaders <> defaultedHeaders

    let headerBytes = CaseInsensitive.mk (Encoding.encodeUtf8 header)

    let valueBytes = Encoding.encodeUtf8 (Text.strip value)

    return (headerBytes, valueBytes)
  where
    requiredHeaders
        | import_ =
            [ ]
        | otherwise =
            (   [ Header{ header = "Accept"      , value = "application/json" }
                ]
            <>  contentType
            )
      where
        contentType
            | body =
                [ Header{ header = "Content-Type", value = "application/json" } ]
            | otherwise =
                [ ]

    defaultedHeaders = case headers of
        Nothing -> []
        Just h -> h

organization :: Maybe Text
organization = Nothing

renderQueryText :: Text -> Maybe [Parameter] -> IO Text
renderQueryText url parameters = do
    let (intermediateURL, queryBytes) = Text.break (== '?') url

    let oldQueryText =
            HTTP.Types.parseQueryText (Encoding.encodeUtf8 queryBytes)

    let oldParameters = do
            (parameter, value) <- oldQueryText

            return Parameter{ parameter, value }

    currentTime <- Time.getPOSIXTime

    let cacheBust =
            [ Parameter
                { parameter = "cachebust"
                , value = Just (Text.pack (show currentTime))
                }
            ]

    let finalParameters = case parameters of
            Nothing -> oldParameters <> cacheBust
            Just newParameters -> oldParameters <> newParameters <> cacheBust

    let queryText = do
            Parameter{ parameter, value } <- finalParameters

            return (parameter, value)

    let builder = HTTP.Types.renderQueryText True queryText

    let bytes = ByteString.Lazy.toStrict (Builder.toLazyByteString builder)

    case Encoding.decodeUtf8' bytes of
        Left exception -> Exception.throwIO exception
        Right text -> return (intermediateURL <> text)


================================================
FILE: src/Grace/Import.hs
================================================
-- | This module contains the import resolution logic
module Grace.Import
    ( -- * Import resolution
      resolve
    , referentiallySane

      -- * Exceptions
    , ResolutionError(..)
    , ImportError(..)
    ) where

import Control.Exception.Safe (Exception(..))
import Data.Bifunctor (first)
import Data.Foldable (foldl')
import Data.HashMap.Strict (HashMap)
import Data.IORef (IORef)
import Data.List.NonEmpty (NonEmpty(..))
import Data.Maybe (fromJust)
import Data.Text (Text)
import Grace.HTTP (HttpException)
import Grace.Input (Input(..), Mode(..))
import Grace.Location (Location(..))
import Grace.Monad (Grace)
import Grace.Syntax (Syntax)
import System.FilePath ((</>))
import Text.URI (Authority, RText, RTextLabel(..))

import qualified Control.Exception.Safe as Exception
import qualified Data.HashMap.Strict as HashMap
import qualified Data.IORef as IORef
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Text as Text
import qualified Data.Text.IO as Text.IO
import qualified Grace.HTTP as HTTP
import qualified Grace.Parser as Parser
import qualified Grace.Pretty as Pretty
import qualified Grace.Syntax as Syntax
import qualified System.Directory as Directory
import qualified System.Environment as Environment
import qualified System.IO.Unsafe as Unsafe
import qualified Text.URI as URI

cache :: IORef (HashMap Text Text)
cache = Unsafe.unsafePerformIO (IORef.newIORef HashMap.empty)
{-# NOINLINE cache #-}

fetch :: Text -> IO Text
fetch url = do
    m <- IORef.readIORef cache

    case HashMap.lookup url m of
        Nothing -> do
            body  <- HTTP.fetch url

            IORef.writeIORef cache $! HashMap.insert url body m

            return body
        Just body -> do
            return body

remoteSchemes :: [RText 'Scheme]
remoteSchemes = map (fromJust . URI.mkScheme) [ "http", "https" ]

-- | Resolve an `Input` by returning the source code that it represents
resolve :: Mode -> Input -> IO (Syntax Location Input)
resolve mode₀ input = case input of
    URI uri mode₁
        | any (`elem` remoteSchemes) (URI.uriScheme uri) -> do
            let name = URI.renderStr uri

            let handler e = throw (HTTPError e)

            text <- Exception.handle handler (fetch (Text.pack name))

            result <- case mode₀ <> mode₁ of
                AsCode -> case Parser.parse name text of
                    Left e -> Exception.throw e
                    Right result -> return result
                AsText -> do
                    return Syntax.Text
                        { chunks = Syntax.Chunks text []
                        , location = 0
                        }
                AsKey -> do
                    return Syntax.Scalar
                        { scalar = Syntax.Key text
                        , location = 0
                        }

            let locate offset = Location{ name, code = text, offset }

            return (first locate result)

        | URI.uriScheme uri == URI.mkScheme "env" -> do
            case URI.uriAuthority uri of
                Left False -> do
                    var <- case URI.uriPath uri of
                        Nothing -> throw MissingPath
                        Just (False, var :| []) -> return (URI.unRText var)
                        _ -> throw UnsupportedPathSeparators

                    maybeCode <- Environment.lookupEnv (Text.unpack var)

                    text <- case maybeCode of
                        Nothing -> throw MissingEnvironmentVariable
                        Just string -> return (Text.pack string)

                    let name = "env:" <> Text.unpack var

                    result <- case mode₀ <> mode₁ of
                        AsCode -> case Parser.parse name text of
                            Left e -> Exception.throw e
                            Right result -> return result
                        AsText -> do
                            return Syntax.Text
                                { chunks = Syntax.Chunks text []
                                , location = 0
                                }
                        AsKey -> do
                            return Syntax.Scalar
                                { scalar = Syntax.Key text
                                , location = 0
                                }

                    let locate offset = Location{ name, code = text, offset }

                    return (first locate result)
                Left True -> do
                    throw UnsupportedPathSeparators
                Right _ -> do
                    throw UnsupportedAuthority

        | URI.uriScheme uri == URI.mkScheme "file" -> do
            if all (== emptyAuthority) (URI.uriAuthority uri)
                then do
                    pieces <- case URI.uriPath uri of
                        Nothing -> throw MissingPath
                        Just (_, pieces) -> return pieces

                    let pathPiecesToFilePath =
                            foldl' (</>) "/" . map (Text.unpack . URI.unRText) . NonEmpty.toList

                    readPath (mode₀ <> mode₁) (pathPiecesToFilePath pieces)
                else do
                    throw UnsupportedAuthority

        | otherwise -> do
            throw InvalidURI

    Path path mode₁ -> do
        readPath (mode₀ <> mode₁) path

    Code name code -> do
        result <- case Parser.parse name code of
            Left e -> Exception.throw e
            Right result -> return result

        let locate offset = Location{ name, code, offset }

        return (first locate result)
  where
    readPath mode path = do
        adjustedPath <- case path of
            '~' : '/' : suffix -> do
                home <- Directory.getHomeDirectory
                return (home </> suffix)
            _ -> do
                return path

        text <- Text.IO.readFile adjustedPath

        result <- case mode of
            AsCode -> case Parser.parse path text of
                Left e -> Exception.throw e
                Right result -> return result
            AsText -> do
                return Syntax.Text
                    { chunks = Syntax.Chunks text []
                    , location = 0
                    }
            AsKey -> do
                return Syntax.Scalar
                    { scalar = Syntax.Key text
                    , location = 0
                    }

        let locate offset = Location{ name = path, code = text, offset }

        return (first locate result)

    throw e = Exception.throw (ImportError input e)

emptyAuthority :: Authority
emptyAuthority = URI.Authority
    { URI.authUserInfo = Nothing
    , URI.authHost = fromJust (URI.mkHost "")
    , URI.authPort = Nothing
    }

remote :: Input -> Bool
remote (URI uri _) = any (`elem` remoteSchemes) (URI.uriScheme uri)
remote _ = False

-- | Fail if the child import tries to access something that the parent import
-- should not have access to
referentiallySane :: Input -> Input -> Grace ()
referentiallySane parent child
    | remote parent && not (remote child) = do
        Exception.throwIO (ImportError parent (ReferentiallyInsane child))
    | otherwise = do
        return ()

-- | The base error for `ImportError` (without the @input@ information)
data ResolutionError
    = HTTPError HttpException
    | InvalidURI
    | MissingEnvironmentVariable
    | MissingPath
    | UnsupportedPathSeparators
    | ReferentiallyInsane Input
    | Unsupporte
Download .txt
gitextract_9bjoz56v/

├── .envrc
├── .gitattributes
├── .gitignore
├── .hlint.yaml
├── .stylish-haskell.yaml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── Setup.hs
├── dependencies/
│   ├── .gitkeep
│   ├── grace-ghcjs.nix
│   ├── grace.nix
│   └── openai.nix
├── doctest/
│   └── Main.hs
├── examples/
│   ├── bluesky/
│   │   └── 2025-11-27.ffg
│   ├── chaining.ffg
│   ├── code.ffg
│   ├── emotion-wheel.ffg
│   ├── eval.ffg
│   ├── history.ffg
│   ├── learn-in-y-minutes.ffg
│   ├── mustache.ffg
│   ├── poem.ffg
│   ├── prompt.ffg
│   ├── taxes.ffg
│   ├── tools.ffg
│   ├── transform-text.ffg
│   ├── tutorial/
│   │   ├── coding.ffg
│   │   ├── conclusion.ffg
│   │   ├── data.ffg
│   │   ├── functions.ffg
│   │   ├── hello.ffg
│   │   ├── html.ffg
│   │   ├── imports.ffg
│   │   ├── prompting.ffg
│   │   └── variables.ffg
│   └── users.ffg
├── flake.nix
├── garnix.yaml
├── ghc/
│   └── Grace/
│       ├── DataFile.hs
│       ├── HTTP.hs
│       └── Width.hs
├── ghcjs/
│   └── Grace/
│       ├── DataFile.hs
│       ├── HTTP.hs
│       ├── Size.hs
│       └── Width.hs
├── grace/
│   └── Main.hs
├── grace.cabal
├── prelude/
│   ├── bool/
│   │   ├── and.ffg
│   │   ├── not.ffg
│   │   ├── or.ffg
│   │   └── package.ffg
│   ├── integer/
│   │   ├── clamp.ffg
│   │   ├── package.ffg
│   │   ├── product.ffg
│   │   ├── sum.ffg
│   │   └── toNatural.ffg
│   ├── list/
│   │   ├── all.ffg
│   │   ├── any.ffg
│   │   ├── concat.ffg
│   │   ├── generate.ffg
│   │   ├── null.ffg
│   │   ├── package.ffg
│   │   ├── reject.ffg
│   │   ├── replicate.ffg
│   │   └── select.ffg
│   ├── natural/
│   │   ├── package.ffg
│   │   ├── product.ffg
│   │   └── sum.ffg
│   ├── package.ffg
│   ├── real/
│   │   ├── package.ffg
│   │   ├── product.ffg
│   │   └── sum.ffg
│   └── text/
│       ├── concat.ffg
│       ├── concatMap.ffg
│       ├── concatMapSep.ffg
│       ├── concatSep.ffg
│       └── package.ffg
├── prompts/
│   ├── abnf.md
│   └── inference.md
├── src/
│   ├── Grace/
│   │   ├── Aeson.hs
│   │   ├── Compat.hs
│   │   ├── Context.hs
│   │   ├── Decode.hs
│   │   ├── Domain.hs
│   │   ├── Encode.hs
│   │   ├── Existential.hs
│   │   ├── GitHub.hs
│   │   ├── HTTP/
│   │   │   └── Type.hs
│   │   ├── Import.hs
│   │   ├── Infer.hs
│   │   ├── Input.hs
│   │   ├── Interpret.hs
│   │   ├── Interpret.hs-boot
│   │   ├── Label.hs
│   │   ├── Location.hs
│   │   ├── Marshal.hs
│   │   ├── Monad.hs
│   │   ├── Monotype.hs
│   │   ├── Normalize.hs
│   │   ├── Parallelizable.hs
│   │   ├── Parser.hs
│   │   ├── Pretty.hs
│   │   ├── Prompt/
│   │   │   └── Types.hs
│   │   ├── Prompt.hs
│   │   ├── REPL.hs
│   │   ├── Syntax.hs
│   │   ├── TH.hs
│   │   ├── Type.hs
│   │   └── Value.hs
│   └── Grace.hs
├── tasty/
│   ├── Main.hs
│   └── data/
│       ├── complex/
│       │   ├── abstract-fold-input.ffg
│       │   ├── abstract-fold-output.ffg
│       │   ├── abstract-fold-type.ffg
│       │   ├── alternative-input.ffg
│       │   ├── alternative-output.ffg
│       │   ├── alternative-type.ffg
│       │   ├── arithmetic-input.ffg
│       │   ├── arithmetic-output.ffg
│       │   ├── arithmetic-type.ffg
│       │   ├── chain-identity-input.ffg
│       │   ├── chain-identity-output.ffg
│       │   ├── chain-identity-type.ffg
│       │   ├── check-function-input.ffg
│       │   ├── check-function-output.ffg
│       │   ├── check-function-type.ffg
│       │   ├── church-encode-list-input.ffg
│       │   ├── church-encode-list-output.ffg
│       │   ├── church-encode-list-type.ffg
│       │   ├── default-arguments-input.ffg
│       │   ├── default-arguments-output.ffg
│       │   ├── default-arguments-type.ffg
│       │   ├── destructure-lambda-input.ffg
│       │   ├── destructure-lambda-output.ffg
│       │   ├── destructure-lambda-type.ffg
│       │   ├── destructure-let-argument-input.ffg
│       │   ├── destructure-let-argument-output.ffg
│       │   ├── destructure-let-argument-type.ffg
│       │   ├── destructure-record-input.ffg
│       │   ├── destructure-record-output.ffg
│       │   ├── destructure-record-type.ffg
│       │   ├── elaborate-inside-optional-input.ffg
│       │   ├── elaborate-inside-optional-output.ffg
│       │   ├── elaborate-inside-optional-type.ffg
│       │   ├── equality-input.ffg
│       │   ├── equality-output.ffg
│       │   ├── equality-type.ffg
│       │   ├── even-input.ffg
│       │   ├── even-output.ffg
│       │   ├── even-type.ffg
│       │   ├── examples-input.ffg
│       │   ├── examples-output.ffg
│       │   ├── examples-type.ffg
│       │   ├── exclaim-input.ffg
│       │   ├── exclaim-output.ffg
│       │   ├── exclaim-type.ffg
│       │   ├── exhaust-type-variable-alphabet-input.ffg
│       │   ├── exhaust-type-variable-alphabet-output.ffg
│       │   ├── exhaust-type-variable-alphabet-type.ffg
│       │   ├── existential-records-input.ffg
│       │   ├── existential-records-output.ffg
│       │   ├── existential-records-type.ffg
│       │   ├── export-show-input.ffg
│       │   ├── export-show-output.ffg
│       │   ├── export-show-type.ffg
│       │   ├── extra-fields-input.ffg
│       │   ├── extra-fields-output.ffg
│       │   ├── extra-fields-type.ffg
│       │   ├── fold-bool-input.ffg
│       │   ├── fold-bool-output.ffg
│       │   ├── fold-bool-type.ffg
│       │   ├── fold-json-input.ffg
│       │   ├── fold-json-output.ffg
│       │   ├── fold-json-type.ffg
│       │   ├── fold-list-input.ffg
│       │   ├── fold-list-output.ffg
│       │   ├── fold-list-type.ffg
│       │   ├── fold-missing-field-input.ffg
│       │   ├── fold-missing-field-output.ffg
│       │   ├── fold-missing-field-type.ffg
│       │   ├── fold-natural-input.ffg
│       │   ├── fold-natural-output.ffg
│       │   ├── fold-natural-type.ffg
│       │   ├── fold-optional-input.ffg
│       │   ├── fold-optional-output.ffg
│       │   ├── fold-optional-type.ffg
│       │   ├── for-annotation-input.ffg
│       │   ├── for-annotation-output.ffg
│       │   ├── for-annotation-type.ffg
│       │   ├── for-check-list-input.ffg
│       │   ├── for-check-list-output.ffg
│       │   ├── for-check-list-type.ffg
│       │   ├── for-lambda-input.ffg
│       │   ├── for-lambda-output.ffg
│       │   ├── for-lambda-type.ffg
│       │   ├── for-list-input.ffg
│       │   ├── for-list-output.ffg
│       │   ├── for-list-type.ffg
│       │   ├── for-naked-output.ffg
│       │   ├── for-naked-type.ffg
│       │   ├── for-optional-input.ffg
│       │   ├── for-optional-output.ffg
│       │   ├── for-optional-type.ffg
│       │   ├── forall-annotation-input.ffg
│       │   ├── forall-annotation-output.ffg
│       │   ├── forall-annotation-type.ffg
│       │   ├── higher-rank-annotation-input.ffg
│       │   ├── higher-rank-annotation-output.ffg
│       │   ├── higher-rank-annotation-type.ffg
│       │   ├── higher-rank-input.ffg
│       │   ├── higher-rank-output.ffg
│       │   ├── higher-rank-subtype-input.ffg
│       │   ├── higher-rank-subtype-output.ffg
│       │   ├── higher-rank-subtype-type.ffg
│       │   ├── higher-rank-type.ffg
│       │   ├── if-then-else-input.ffg
│       │   ├── if-then-else-output.ffg
│       │   ├── if-then-else-type.ffg
│       │   ├── import-github-input.ffg
│       │   ├── import-github-output.ffg
│       │   ├── import-github-type.ffg
│       │   ├── impredicative-const-input.ffg
│       │   ├── impredicative-const-output.ffg
│       │   ├── impredicative-const-type.ffg
│       │   ├── index-input.ffg
│       │   ├── index-output.ffg
│       │   ├── index-type.ffg
│       │   ├── inequality-input.ffg
│       │   ├── inequality-output.ffg
│       │   ├── inequality-type.ffg
│       │   ├── infer-higher-order-input.ffg
│       │   ├── infer-higher-order-output.ffg
│       │   ├── infer-higher-order-type.ffg
│       │   ├── infer-reverse-input.ffg
│       │   ├── infer-reverse-output.ffg
│       │   ├── infer-reverse-type.ffg
│       │   ├── inner-forall-input.ffg
│       │   ├── inner-forall-output.ffg
│       │   ├── inner-forall-type.ffg
│       │   ├── instantiate-nested-type-input.ffg
│       │   ├── instantiate-nested-type-output.ffg
│       │   ├── instantiate-nested-type-type.ffg
│       │   ├── instantiate-variable-input.ffg
│       │   ├── instantiate-variable-output.ffg
│       │   ├── instantiate-variable-type.ffg
│       │   ├── interpolate-default-input.ffg
│       │   ├── interpolate-default-output.ffg
│       │   ├── interpolate-default-type.ffg
│       │   ├── interpolation-input.ffg
│       │   ├── interpolation-output.ffg
│       │   ├── interpolation-type.ffg
│       │   ├── json-fold-input.ffg
│       │   ├── json-fold-output.ffg
│       │   ├── json-fold-type.ffg
│       │   ├── keyword-prefix-input.ffg
│       │   ├── keyword-prefix-output.ffg
│       │   ├── keyword-prefix-type.ffg
│       │   ├── lambda-annotation-input.ffg
│       │   ├── lambda-annotation-output.ffg
│       │   ├── lambda-annotation-type.ffg
│       │   ├── let-functions-input.ffg
│       │   ├── let-functions-output.ffg
│       │   ├── let-functions-type.ffg
│       │   ├── list-append-subtype-input.ffg
│       │   ├── list-append-subtype-output.ffg
│       │   ├── list-append-subtype-type.ffg
│       │   ├── list-of-alternatives-input.ffg
│       │   ├── list-of-alternatives-output.ffg
│       │   ├── list-of-alternatives-type.ffg
│       │   ├── list-unequal-length-input.ffg
│       │   ├── list-unequal-length-output.ffg
│       │   ├── list-unequal-length-type.ffg
│       │   ├── merge-multiple-input.ffg
│       │   ├── merge-multiple-output.ffg
│       │   ├── merge-multiple-type.ffg
│       │   ├── merge-optional-input.ffg
│       │   ├── merge-optional-output.ffg
│       │   ├── merge-optional-type.ffg
│       │   ├── minus-natural-input.ffg
│       │   ├── minus-natural-output.ffg
│       │   ├── minus-natural-type.ffg
│       │   ├── modulus-input.ffg
│       │   ├── modulus-output.ffg
│       │   ├── modulus-type.ffg
│       │   ├── nested-arithmetic-input.ffg
│       │   ├── nested-arithmetic-output.ffg
│       │   ├── nested-arithmetic-type.ffg
│       │   ├── nested-constructor-input.ffg
│       │   ├── nested-constructor-output.ffg
│       │   ├── nested-constructor-type.ffg
│       │   ├── nested-fields-input.ffg
│       │   ├── nested-fields-output.ffg
│       │   ├── nested-fields-type.ffg
│       │   ├── nested-let-input.ffg
│       │   ├── nested-let-output.ffg
│       │   ├── nested-let-type.ffg
│       │   ├── numeric-subtyping-input.ffg
│       │   ├── numeric-subtyping-output.ffg
│       │   ├── numeric-subtyping-type.ffg
│       │   ├── optional-fields-input.ffg
│       │   ├── optional-fields-output.ffg
│       │   ├── optional-fields-type.ffg
│       │   ├── package-input.ffg
│       │   ├── package-output.ffg
│       │   ├── package-type.ffg
│       │   ├── pattern-input.ffg
│       │   ├── pattern-output.ffg
│       │   ├── pattern-type.ffg
│       │   ├── polymorphic-instantiation-input.ffg
│       │   ├── polymorphic-instantiation-output.ffg
│       │   ├── polymorphic-instantiation-type.ffg
│       │   ├── polymorphic-let-input.ffg
│       │   ├── polymorphic-let-output.ffg
│       │   ├── polymorphic-let-type.ffg
│       │   ├── polymorphic-subsumption-input.ffg
│       │   ├── polymorphic-subsumption-output.ffg
│       │   ├── polymorphic-subsumption-type.ffg
│       │   ├── potpourri-input.ffg
│       │   ├── potpourri-output.ffg
│       │   ├── potpourri-type.ffg
│       │   ├── predicative-null-input.ffg
│       │   ├── predicative-null-output.ffg
│       │   ├── predicative-null-type.ffg
│       │   ├── prelude-input.ffg
│       │   ├── prelude-output.ffg
│       │   ├── prelude-type.ffg
│       │   ├── preserve-row-input.ffg
│       │   ├── preserve-row-output.ffg
│       │   ├── preserve-row-type.ffg
│       │   ├── principle-of-explosion-input.ffg
│       │   ├── principle-of-explosion-output.ffg
│       │   ├── principle-of-explosion-type.ffg
│       │   ├── quantifiers-input.ffg
│       │   ├── quantifiers-output.ffg
│       │   ├── quantifiers-type.ffg
│       │   ├── quoted-label-input.ffg
│       │   ├── quoted-label-output.ffg
│       │   ├── quoted-label-type.ffg
│       │   ├── read-input.ffg
│       │   ├── read-output.ffg
│       │   ├── read-type.ffg
│       │   ├── record-mismatch-both-extra-input.ffg
│       │   ├── record-mismatch-both-extra-output.ffg
│       │   ├── record-mismatch-both-extra-type.ffg
│       │   ├── record-mismatch-left-extra-input.ffg
│       │   ├── record-mismatch-left-extra-output.ffg
│       │   ├── record-mismatch-left-extra-type.ffg
│       │   ├── record-mismatch-right-extra-input.ffg
│       │   ├── record-mismatch-right-extra-output.ffg
│       │   ├── record-mismatch-right-extra-type.ffg
│       │   ├── record-mismatch-two-extra-input.ffg
│       │   ├── record-mismatch-two-extra-output.ffg
│       │   ├── record-mismatch-two-extra-type.ffg
│       │   ├── remote-transitive-import-input.ffg
│       │   ├── remote-transitive-import-output.ffg
│       │   ├── remote-transitive-import-type.ffg
│       │   ├── restrict-project-input.ffg
│       │   ├── restrict-project-output.ffg
│       │   ├── restrict-project-type.ffg
│       │   ├── separators-input.ffg
│       │   ├── separators-output.ffg
│       │   ├── separators-type.ffg
│       │   ├── show-input.ffg
│       │   ├── show-output.ffg
│       │   ├── show-type.ffg
│       │   ├── skolem-function-type-input.ffg
│       │   ├── skolem-function-type-stderr.txt
│       │   ├── slice-input.ffg
│       │   ├── slice-output.ffg
│       │   ├── slice-type.ffg
│       │   ├── solve-fields-input.ffg
│       │   ├── solve-fields-output.ffg
│       │   ├── solve-fields-type.ffg
│       │   ├── solve-unused-impredicative-polymorphism-input.ffg
│       │   ├── solve-unused-impredicative-polymorphism-output.ffg
│       │   ├── solve-unused-impredicative-polymorphism-type.ffg
│       │   ├── subtype-function-input.ffg
│       │   ├── subtype-function-output.ffg
│       │   ├── subtype-function-type.ffg
│       │   ├── triple-field-input.ffg
│       │   ├── triple-field-output.ffg
│       │   ├── triple-field-type.ffg
│       │   ├── triple-supertype-input.ffg
│       │   ├── triple-supertype-output.ffg
│       │   ├── triple-supertype-type.ffg
│       │   ├── unbound-alternatives-variable-input.ffg
│       │   ├── unbound-alternatives-variable-stderr.txt
│       │   ├── unbound-fields-variable-input.ffg
│       │   ├── unbound-fields-variable-stderr.txt
│       │   ├── undo-elaboration-input.ffg
│       │   ├── undo-elaboration-output.ffg
│       │   ├── undo-elaboration-type.ffg
│       │   ├── unify-json-input.ffg
│       │   ├── unify-json-output.ffg
│       │   ├── unify-json-type.ffg
│       │   ├── unify-null-input.ffg
│       │   ├── unify-null-output.ffg
│       │   ├── unify-null-type.ffg
│       │   ├── unify-unsolved-input.ffg
│       │   ├── unify-unsolved-output.ffg
│       │   ├── unify-unsolved-type.ffg
│       │   ├── unused-impredicative-polymorphism-input.ffg
│       │   ├── unused-impredicative-polymorphism-output.ffg
│       │   ├── unused-impredicative-polymorphism-type.ffg
│       │   ├── unused-polymorphism-input.ffg
│       │   ├── unused-polymorphism-output.ffg
│       │   ├── unused-polymorphism-type.ffg
│       │   ├── use-equation-input.ffg
│       │   ├── use-equation-output.ffg
│       │   ├── use-equation-type.ffg
│       │   ├── well-formed-input.ffg
│       │   ├── well-formed-output.ffg
│       │   ├── well-formed-type.ffg
│       │   ├── yaml-input.ffg
│       │   ├── yaml-output.ffg
│       │   └── yaml-type.ffg
│       ├── error/
│       │   ├── for-naked-input.ffg
│       │   ├── for-naked-stderr.txt
│       │   ├── parse/
│       │   │   ├── eof-input.ffg
│       │   │   ├── eof-stderr.txt
│       │   │   ├── incomplete-escape-input.ffg
│       │   │   ├── incomplete-escape-stderr.txt
│       │   │   ├── incomplete-interpolation-input.ffg
│       │   │   ├── incomplete-interpolation-stderr.txt
│       │   │   ├── incomplete-string-input.ffg
│       │   │   ├── incomplete-string-stderr.txt
│       │   │   ├── invalid-multiline-input.ffg
│       │   │   ├── invalid-multiline-stderr.txt
│       │   │   ├── unbalanced-bracket-input.ffg
│       │   │   ├── unbalanced-bracket-stderr.txt
│       │   │   ├── unrecognized-symbol-input.ffg
│       │   │   └── unrecognized-symbol-stderr.txt
│       │   ├── resolve/
│       │   │   ├── empty-environment-variable-input.ffg
│       │   │   ├── empty-environment-variable-stderr.txt
│       │   │   ├── environment-authority-input.ffg
│       │   │   ├── environment-authority-stderr.txt
│       │   │   ├── environment-leading-slash-input.ffg
│       │   │   ├── environment-leading-slash-stderr.txt
│       │   │   ├── environment-missing-input.ffg
│       │   │   ├── environment-missing-stderr.txt
│       │   │   ├── environment-variable-slash-input.ffg
│       │   │   ├── environment-variable-slash-stderr.txt
│       │   │   ├── file-authority-input.ffg
│       │   │   ├── file-authority-stderr.txt
│       │   │   ├── file-missing-path-input.ffg
│       │   │   ├── file-missing-path-stderr.txt
│       │   │   ├── host-not-found-input.ffg
│       │   │   ├── host-not-found-stderr.txt
│       │   │   ├── invalid-destination-host-input.ffg
│       │   │   ├── invalid-destination-host-stderr.txt
│       │   │   ├── invalid-utf8-input.ffg
│       │   │   ├── invalid-utf8-stderr.txt
│       │   │   ├── read-invalid-json-input.ffg
│       │   │   ├── read-invalid-json-stderr.txt
│       │   │   ├── remote-invalid-parse-input.ffg
│       │   │   ├── remote-invalid-parse-stderr.txt
│       │   │   ├── remote-referentially-insane-input.ffg
│       │   │   └── remote-referentially-insane-stderr.txt
│       │   └── type/
│       │       ├── alternatives-variable-input.ffg
│       │       ├── alternatives-variable-stderr.txt
│       │       ├── bad-type-annotation-input.ffg
│       │       ├── bad-type-annotation-stderr.txt
│       │       ├── destructure-missing-required-input.ffg
│       │       ├── destructure-missing-required-stderr.txt
│       │       ├── dynamic-scope-input.ffg
│       │       ├── dynamic-scope-stderr.txt
│       │       ├── fields-variable-mismatch-input.ffg
│       │       ├── fields-variable-mismatch-stderr.txt
│       │       ├── fold-missing-field-input.ffg
│       │       ├── fold-missing-field-stderr.txt
│       │       ├── fold-typo-field-input.ffg
│       │       ├── fold-typo-field-stderr.txt
│       │       ├── for-annotation-input.ffg
│       │       ├── for-annotation-stderr.txt
│       │       ├── for-mixed-input.ffg
│       │       ├── for-mixed-stderr.txt
│       │       ├── impredicative-instantiation-input.ffg
│       │       ├── impredicative-instantiation-stderr.txt
│       │       ├── impredicative-polymorphism-input.ffg
│       │       ├── impredicative-polymorphism-output.ffg
│       │       ├── impredicative-polymorphism-type.ffg
│       │       ├── index-slice-input.ffg
│       │       ├── index-slice-stderr.txt
│       │       ├── invalid-argument-input.ffg
│       │       ├── invalid-argument-stderr.txt
│       │       ├── invalid-handler-input.ffg
│       │       ├── invalid-handler-stderr.txt
│       │       ├── invalid-plus-annotation-input.ffg
│       │       ├── invalid-plus-annotation-stderr.txt
│       │       ├── invalid-plus-operands-input.ffg
│       │       ├── invalid-plus-operands-stderr.txt
│       │       ├── invalid-times-annotation-input.ffg
│       │       ├── invalid-times-annotation-stderr.txt
│       │       ├── invalid-times-operands-input.ffg
│       │       ├── invalid-times-operands-stderr.txt
│       │       ├── merge-wrong-type-input.ffg
│       │       ├── merge-wrong-type-stderr.txt
│       │       ├── missing-required-field-input.ffg
│       │       ├── missing-required-field-stderr.txt
│       │       ├── modulus-abstract-input.ffg
│       │       ├── modulus-abstract-stderr.txt
│       │       ├── modulus-zero-input.ffg
│       │       ├── modulus-zero-stderr.txt
│       │       ├── non-singular-pattern-input.ffg
│       │       ├── non-singular-pattern-stderr.txt
│       │       ├── not-a-function-input.ffg
│       │       ├── not-a-function-stderr.txt
│       │       ├── occurs-alternatives-input.ffg
│       │       ├── occurs-alternatives-stderr.txt
│       │       ├── occurs-type-backwards-input.ffg
│       │       ├── occurs-type-backwards-stderr.txt
│       │       ├── occurs-type-input.ffg
│       │       ├── occurs-type-stderr.txt
│       │       ├── pattern-input.ffg
│       │       ├── pattern-stderr.txt
│       │       ├── pin-fields-variable-input.ffg
│       │       ├── pin-fields-variable-stderr.txt
│       │       ├── read-fail-input.ffg
│       │       ├── read-fail-stderr.txt
│       │       ├── read-input.ffg
│       │       ├── read-stderr.txt
│       │       ├── show-key-input.ffg
│       │       ├── show-key-stderr.txt
│       │       ├── subtype-function-input.ffg
│       │       ├── subtype-function-stderr.txt
│       │       ├── type-mismatch-input.ffg
│       │       ├── type-mismatch-stderr.txt
│       │       ├── unbound-input.ffg
│       │       ├── unbound-stderr.txt
│       │       ├── unbound-type-input.ffg
│       │       ├── unbound-type-stderr.txt
│       │       ├── union-mismatch-both-extra-input.ffg
│       │       ├── union-mismatch-both-extra-stderr.txt
│       │       ├── union-mismatch-left-extra-input.ffg
│       │       ├── union-mismatch-left-extra-stderr.txt
│       │       ├── union-mismatch-right-extra-input.ffg
│       │       ├── union-mismatch-right-extra-output.ffg
│       │       ├── union-mismatch-right-extra-type.ffg
│       │       ├── union-variable-mismatch-input.ffg
│       │       ├── union-variable-mismatch-stderr.txt
│       │       ├── variable-mismatch-input.ffg
│       │       └── variable-mismatch-stderr.txt
│       ├── json/
│       │   ├── colors-input.ffg
│       │   ├── colors-output.ffg
│       │   ├── colors-type.ffg
│       │   ├── colors.json
│       │   ├── geoip-input.ffg
│       │   ├── geoip-output.ffg
│       │   ├── geoip-type.ffg
│       │   ├── geoip.json
│       │   ├── glossary-input.ffg
│       │   ├── glossary-output.ffg
│       │   ├── glossary-type.ffg
│       │   ├── glossary.json
│       │   ├── markers-input.ffg
│       │   ├── markers-output.ffg
│       │   ├── markers-type.ffg
│       │   ├── markers.json
│       │   ├── menu-input.ffg
│       │   ├── menu-output.ffg
│       │   ├── menu-type.ffg
│       │   ├── menu.json
│       │   ├── menu2-input.ffg
│       │   ├── menu2-output.ffg
│       │   ├── menu2-type.ffg
│       │   ├── menu2.json
│       │   ├── numbers-input.ffg
│       │   ├── numbers-output.ffg
│       │   ├── numbers-type.ffg
│       │   ├── numbers.json
│       │   ├── people-input.ffg
│       │   ├── people-output.ffg
│       │   ├── people-type.ffg
│       │   ├── people.json
│       │   ├── products-input.ffg
│       │   ├── products-output.ffg
│       │   ├── products-type.ffg
│       │   ├── products.json
│       │   ├── recipes-input.ffg
│       │   ├── recipes-output.ffg
│       │   ├── recipes-type.ffg
│       │   ├── recipes.json
│       │   ├── server-input.ffg
│       │   ├── server-output.ffg
│       │   ├── server-type.ffg
│       │   ├── server.json
│       │   ├── twitter-input.ffg
│       │   ├── twitter-output.ffg
│       │   ├── twitter-type.ffg
│       │   ├── twitter.json
│       │   ├── widget-input.ffg
│       │   ├── widget-output.ffg
│       │   ├── widget-type.ffg
│       │   ├── widget.json
│       │   ├── youtube-input.ffg
│       │   ├── youtube-output.ffg
│       │   ├── youtube-type.ffg
│       │   └── youtube.json
│       ├── true.ffg
│       └── unit/
│           ├── add-integer-input.ffg
│           ├── add-integer-output.ffg
│           ├── add-integer-type.ffg
│           ├── alternative-input.ffg
│           ├── alternative-output.ffg
│           ├── alternative-type.ffg
│           ├── and-input.ffg
│           ├── and-output.ffg
│           ├── and-type.ffg
│           ├── append-list-input.ffg
│           ├── append-list-output.ffg
│           ├── append-list-type.ffg
│           ├── append-text-input.ffg
│           ├── append-text-output.ffg
│           ├── append-text-type.ffg
│           ├── application-input.ffg
│           ├── application-output.ffg
│           ├── application-type.ffg
│           ├── bool-input.ffg
│           ├── bool-output.ffg
│           ├── bool-type.ffg
│           ├── default-argument-input.ffg
│           ├── default-argument-output.ffg
│           ├── default-argument-type.ffg
│           ├── default-record-argument-input.ffg
│           ├── default-record-argument-output.ffg
│           ├── default-record-argument-type.ffg
│           ├── destructure-lambda-input.ffg
│           ├── destructure-lambda-output.ffg
│           ├── destructure-lambda-type.ffg
│           ├── destructure-let-argument-input.ffg
│           ├── destructure-let-argument-output.ffg
│           ├── destructure-let-argument-type.ffg
│           ├── destructure-record-input.ffg
│           ├── destructure-record-output.ffg
│           ├── destructure-record-type.ffg
│           ├── divide-input.ffg
│           ├── divide-output.ffg
│           ├── divide-type.ffg
│           ├── equal-input.ffg
│           ├── equal-output.ffg
│           ├── equal-type.ffg
│           ├── export-show-input.ffg
│           ├── export-show-output.ffg
│           ├── export-show-type.ffg
│           ├── field-input.ffg
│           ├── field-output.ffg
│           ├── field-type.ffg
│           ├── fields-input.ffg
│           ├── fields-output.ffg
│           ├── fields-type.ffg
│           ├── for-list-input.ffg
│           ├── for-list-output.ffg
│           ├── for-list-type.ffg
│           ├── for-optional-input.ffg
│           ├── for-optional-output.ffg
│           ├── for-optional-type.ffg
│           ├── greater-than-input.ffg
│           ├── greater-than-or-equal-input.ffg
│           ├── greater-than-or-equal-output.ffg
│           ├── greater-than-or-equal-type.ffg
│           ├── greater-than-output.ffg
│           ├── greater-than-type.ffg
│           ├── if-input.ffg
│           ├── if-output.ffg
│           ├── if-type.ffg
│           ├── import-input.ffg
│           ├── import-output.ffg
│           ├── import-type.ffg
│           ├── index-input.ffg
│           ├── index-output.ffg
│           ├── index-type.ffg
│           ├── inequality-input.ffg
│           ├── inequality-output.ffg
│           ├── inequality-type.ffg
│           ├── integer-abs-input.ffg
│           ├── integer-abs-output.ffg
│           ├── integer-abs-type.ffg
│           ├── integer-input.ffg
│           ├── integer-output.ffg
│           ├── integer-type.ffg
│           ├── interpolation-input.ffg
│           ├── interpolation-output.ffg
│           ├── interpolation-type.ffg
│           ├── json-input.ffg
│           ├── json-output.ffg
│           ├── json-subtype-input.ffg
│           ├── json-subtype-output.ffg
│           ├── json-subtype-type.ffg
│           ├── json-type.ffg
│           ├── key-input.ffg
│           ├── key-output.ffg
│           ├── key-type.ffg
│           ├── lambda-annotation-input.ffg
│           ├── lambda-annotation-output.ffg
│           ├── lambda-annotation-type.ffg
│           ├── lambda-input.ffg
│           ├── lambda-output.ffg
│           ├── lambda-type.ffg
│           ├── less-than-input.ffg
│           ├── less-than-or-equal-input.ffg
│           ├── less-than-or-equal-output.ffg
│           ├── less-than-or-equal-type.ffg
│           ├── less-than-output.ffg
│           ├── less-than-type.ffg
│           ├── let-default-input.ffg
│           ├── let-default-output.ffg
│           ├── let-default-type.ffg
│           ├── let-function-input.ffg
│           ├── let-function-output.ffg
│           ├── let-function-type.ffg
│           ├── let-input.ffg
│           ├── let-output.ffg
│           ├── let-type.ffg
│           ├── list-fold-input.ffg
│           ├── list-fold-output.ffg
│           ├── list-fold-type.ffg
│           ├── list-indexed-input.ffg
│           ├── list-indexed-output.ffg
│           ├── list-indexed-type.ffg
│           ├── list-input.ffg
│           ├── list-length-input.ffg
│           ├── list-length-output.ffg
│           ├── list-length-type.ffg
│           ├── list-map-input.ffg
│           ├── list-map-output.ffg
│           ├── list-map-type.ffg
│           ├── list-output.ffg
│           ├── list-type.ffg
│           ├── merge-bool-input.ffg
│           ├── merge-bool-output.ffg
│           ├── merge-bool-type.ffg
│           ├── merge-evaluation-input.ffg
│           ├── merge-evaluation-output.ffg
│           ├── merge-evaluation-type.ffg
│           ├── merge-input.ffg
│           ├── merge-optional-input.ffg
│           ├── merge-optional-output.ffg
│           ├── merge-optional-type.ffg
│           ├── merge-output.ffg
│           ├── merge-type.ffg
│           ├── minus-input.ffg
│           ├── minus-output.ffg
│           ├── minus-type.ffg
│           ├── missing-field-input.ffg
│           ├── missing-field-output.ffg
│           ├── missing-field-type.ffg
│           ├── missing-optional-field-input.ffg
│           ├── missing-optional-field-output.ffg
│           ├── missing-optional-field-type.ffg
│           ├── modulus-input.ffg
│           ├── modulus-output.ffg
│           ├── modulus-type.ffg
│           ├── multi-lambda-input.ffg
│           ├── multi-lambda-output.ffg
│           ├── multi-lambda-type.ffg
│           ├── multiline-input.ffg
│           ├── multiline-output.ffg
│           ├── multiline-type.ffg
│           ├── multiple-path-components-input.ffg
│           ├── multiple-path-components-output.ffg
│           ├── multiple-path-components-type.ffg
│           ├── natural-fold-input.ffg
│           ├── natural-fold-output.ffg
│           ├── natural-fold-type.ffg
│           ├── natural-input.ffg
│           ├── natural-output.ffg
│           ├── natural-type.ffg
│           ├── not-equal-input.ffg
│           ├── not-equal-output.ffg
│           ├── not-equal-type.ffg
│           ├── null-input.ffg
│           ├── null-output.ffg
│           ├── null-type.ffg
│           ├── or-input.ffg
│           ├── or-output.ffg
│           ├── or-type.ffg
│           ├── pattern-input.ffg
│           ├── pattern-output.ffg
│           ├── pattern-type.ffg
│           ├── plus-input.ffg
│           ├── plus-output.ffg
│           ├── plus-type.ffg
│           ├── positive-integer-input.ffg
│           ├── positive-integer-output.ffg
│           ├── positive-integer-type.ffg
│           ├── pun-input.ffg
│           ├── pun-output.ffg
│           ├── pun-type.ffg
│           ├── quoted-label-input.ffg
│           ├── quoted-label-output.ffg
│           ├── quoted-label-type.ffg
│           ├── read-input.ffg
│           ├── read-output.ffg
│           ├── read-type.ffg
│           ├── real-dot-zero-input.ffg
│           ├── real-dot-zero-output.ffg
│           ├── real-dot-zero-type.ffg
│           ├── real-input.ffg
│           ├── real-output.ffg
│           ├── real-type.ffg
│           ├── record-desugar-input.ffg
│           ├── record-desugar-output.ffg
│           ├── record-desugar-type.ffg
│           ├── record-input.ffg
│           ├── record-output.ffg
│           ├── record-type.ffg
│           ├── remote-import-input.ffg
│           ├── remote-import-output.ffg
│           ├── remote-import-type.ffg
│           ├── reveal-input.ffg
│           ├── reveal-output.ffg
│           ├── reveal-type.ffg
│           ├── show-input.ffg
│           ├── show-output.ffg
│           ├── show-type.ffg
│           ├── slice-input.ffg
│           ├── slice-output.ffg
│           ├── slice-type.ffg
│           ├── string-labels-input.ffg
│           ├── string-labels-output.ffg
│           ├── string-labels-type.ffg
│           ├── text-escaped-input.ffg
│           ├── text-escaped-output.ffg
│           ├── text-escaped-type.ffg
│           ├── text-input.ffg
│           ├── text-output.ffg
│           ├── text-type.ffg
│           ├── times-input.ffg
│           ├── times-output.ffg
│           ├── times-type.ffg
│           ├── union-input.ffg
│           ├── union-output.ffg
│           ├── union-type.ffg
│           ├── visible-row-quantification-input.ffg
│           ├── visible-row-quantification-output.ffg
│           ├── visible-row-quantification-type.ffg
│           ├── visible-variant-quantification-input.ffg
│           ├── visible-variant-quantification-output.ffg
│           ├── visible-variant-quantification-type.ffg
│           ├── yaml-input.ffg
│           ├── yaml-output.ffg
│           └── yaml-type.ffg
├── try-grace/
│   └── Main.hs
└── website/
    ├── css/
    │   └── grace.css
    └── index.html
Condensed preview — 847 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,023K chars).
[
  {
    "path": ".envrc",
    "chars": 10,
    "preview": "use flake\n"
  },
  {
    "path": ".gitattributes",
    "chars": 32,
    "preview": "*.ffg linguist-language=Haskell\n"
  },
  {
    "path": ".gitignore",
    "chars": 265,
    "preview": "dist\ndist-*\ncabal-dev\n*.o\n*.hi\n*.hie\n*.chi\n*.chs.h\n*.dyn_o\n*.dyn_hi\n.hpc\n.hsenv\n.cabal-sandbox/\ncabal.sandbox.config\n*.p"
  },
  {
    "path": ".hlint.yaml",
    "chars": 377,
    "preview": "- ignore: {name: Use fmap}\n- ignore: {name: Use <$>}\n- ignore: {name: Avoid lambda using `infix`}\n- ignore: {name: Use i"
  },
  {
    "path": ".stylish-haskell.yaml",
    "chars": 274,
    "preview": "steps:\n  - module_header:\n      open_bracket: next_line\n      separate_lists: false\n      sort: false\n  - imports:\n     "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5222,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 6406,
    "preview": "# Contributing to Grace\n\nThis document explains how to edit, build, and run this project, both if you\nwant to change you"
  },
  {
    "path": "LICENSE",
    "chars": 1478,
    "preview": "Copyright (c) 2021 Gabriella Gonzalez\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or w"
  },
  {
    "path": "README.md",
    "chars": 7178,
    "preview": "# Grace\n\n[![built with garnix](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgarnix.io%2Fapi%2Fbadges%2FGabriell"
  },
  {
    "path": "Setup.hs",
    "chars": 91,
    "preview": "import qualified Distribution.Simple\n\nmain :: IO ()\nmain = Distribution.Simple.defaultMain\n"
  },
  {
    "path": "dependencies/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "dependencies/grace-ghcjs.nix",
    "chars": 1467,
    "preview": "{ mkDerivation, aeson, aeson-pretty, aeson-yaml, async, base\n, binary, bytestring, case-insensitive, containers, directo"
  },
  {
    "path": "dependencies/grace.nix",
    "chars": 1445,
    "preview": "{ mkDerivation, aeson, aeson-pretty, aeson-yaml, ansi-terminal\n, async, base, bytestring, case-insensitive, containers, "
  },
  {
    "path": "dependencies/openai.nix",
    "chars": 1128,
    "preview": "{ mkDerivation, aeson, base, bytestring, containers, fetchgit\n, filepath, http-api-data, http-client, http-client-tls, h"
  },
  {
    "path": "doctest/Main.hs",
    "chars": 837,
    "preview": "import qualified Test.DocTest\n\nmain :: IO ()\nmain = Test.DocTest.doctest\n    [ \"-idist/build/autogen\"\n    , \"--fast\"\n   "
  },
  {
    "path": "examples/bluesky/2025-11-27.ffg",
    "chars": 180,
    "preview": "{ \"How are you feeling?\": fold\n    { \"Hungry\" { }: 'How hungry are you?'\n        (fold{ succ text: \"😋\" + text, zero: \"\" "
  },
  {
    "path": "examples/chaining.ffg",
    "chars": 330,
    "preview": "\\{ \"OpenAI API key\" } ->\n\nlet key = .'OpenAI API key'\n\nlet question = prompt\n        { key\n        , text: \"Give me a qu"
  },
  {
    "path": "examples/code.ffg",
    "chars": 122,
    "preview": "\\{ \"OpenAI API key\" } ->\n\nlet key = .'OpenAI API key'\n\nin  import prompt{ key, text: \"Uppercase the input\" } \"abc\" : Tex"
  },
  {
    "path": "examples/emotion-wheel.ffg",
    "chars": 2327,
    "preview": "# Ask ChatGPT how it feels using this emotion wheel:\n#\n# https://www.betterup.com/hs-fs/hubfs/Emotion-Wheel-I.jpg?width="
  },
  {
    "path": "examples/eval.ffg",
    "chars": 318,
    "preview": "# Grace supports \"eval\", meaning that you can import code from strings (even\n# dynamic strings which depend on values in"
  },
  {
    "path": "examples/history.ffg",
    "chars": 2995,
    "preview": "\\arguments ->\n\nlet key = arguments.\"OpenAI API key\" in\n\nprompt\n    { key\n    , history:\n        [ User{ name: \"gabby\", t"
  },
  {
    "path": "examples/learn-in-y-minutes.ffg",
    "chars": 6837,
    "preview": "# Single-line comment\n\n# \"Multi-line\" comment\n#\n# Grace doesn't have special syntax for multi-line comments.  Instead, G"
  },
  {
    "path": "examples/mustache.ffg",
    "chars": 455,
    "preview": "# This is taken from the mustache tutorial:\n#\n# https://mustache.github.io/mustache.5.html\nlet template args = \"\n       "
  },
  {
    "path": "examples/poem.ffg",
    "chars": 1787,
    "preview": "\\{ \"OpenAI API key\" } ->\n\nlet key = .'OpenAI API key'\n\nlet concatSep = import github\n      { owner: \"Gabriella439\"\n     "
  },
  {
    "path": "examples/prompt.ffg",
    "chars": 149,
    "preview": "\\{ \"OpenAI API key\" } ->\n\nlet key = .'OpenAI API key'\n\nlet { x, y } = prompt{ key, text: \"Give me two numbers\" }\n\nin  { "
  },
  {
    "path": "examples/taxes.ffg",
    "chars": 3288,
    "preview": "\\{ \"Filing status\", \"Taxable income\" } ->\n\nlet toBracket brackets (_ : { }) income = fold\n        { cons: \\bracket resul"
  },
  {
    "path": "examples/tools.ffg",
    "chars": 417,
    "preview": "\\{ \"OpenAI API key\" } ->\n\nlet key = .'OpenAI API key'\n\nlet concatSep = import github\n      { owner: \"Gabriella439\"\n     "
  },
  {
    "path": "examples/transform-text.ffg",
    "chars": 253,
    "preview": "\\{ \"OpenAI API key\", passage, code } ->\n\nprompt\n  { key: .'OpenAI API key'\n  , text: \"\n      Take this passage:\n\n      $"
  },
  {
    "path": "examples/tutorial/coding.ffg",
    "chars": 559,
    "preview": "\\arguments ->\n\nlet key = arguments.\"OpenAI API key\" in\n\n# What do you think this code will do?  Run it to test your gues"
  },
  {
    "path": "examples/tutorial/conclusion.ffg",
    "chars": 606,
    "preview": "# Function arguments and variables can have spaces and punctuation if you quote\n# them like this (with a leading `.` fol"
  },
  {
    "path": "examples/tutorial/data.ffg",
    "chars": 1096,
    "preview": "# A record will render as a definition list when converted to HTML\n\n{ \"An example string\": \"Mercury\"\n, \"An example strin"
  },
  {
    "path": "examples/tutorial/functions.ffg",
    "chars": 1212,
    "preview": "# You can also define functions using `let` expressions:\nlet greet{ name } = \"Hello, ${name}!\"\n\nlet greeting = greet{ na"
  },
  {
    "path": "examples/tutorial/hello.ffg",
    "chars": 423,
    "preview": "# This is a tour of the Fall-from-Grace language (a.k.a. \"Grace\" for short).\n#\n# First, any line prefixed with a \"#\" cha"
  },
  {
    "path": "examples/tutorial/html.ffg",
    "chars": 286,
    "preview": "# This Grace browser attempts to faithfully render any Grace expression\n# as an equivalent HTML representation.  For exa"
  },
  {
    "path": "examples/tutorial/imports.ffg",
    "chars": 2277,
    "preview": "{ \"URL import\":\n    # You can import Grace expressions by their URL.  For example, the following\n    # URL encodes a fun"
  },
  {
    "path": "examples/tutorial/prompting.ffg",
    "chars": 1291,
    "preview": "# Grace provides built-in language support for LLMs using the `prompt` function.\n# To run these examples you will need t"
  },
  {
    "path": "examples/tutorial/variables.ffg",
    "chars": 473,
    "preview": "# You can define a variable using `let`:\nlet john = { name: \"John Doe\", age: 24 }\n\n# Variables can reference earlier var"
  },
  {
    "path": "examples/users.ffg",
    "chars": 320,
    "preview": "# Grace version of an example from https://dhall-lang.org/\nlet makeUser{ user }=\n      let home = \"/home/${user}\"\n\n     "
  },
  {
    "path": "flake.nix",
    "chars": 14904,
    "preview": "{\n  inputs = {\n    garnix-lib.url = \"github:garnix-io/garnix-lib\";\n\n    nixpkgs.url = github:NixOS/nixpkgs/24.11;\n\n    u"
  },
  {
    "path": "garnix.yaml",
    "chars": 623,
    "preview": "builds:\n exclude: []\n include:\n - packages.aarch64-darwin.default\n - packages.x86_64-linux.default\n - packages.aarch64-l"
  },
  {
    "path": "ghc/Grace/DataFile.hs",
    "chars": 461,
    "preview": "{-| This module provides a uniform interface for accessing data files using\n    both GHC and GHCJS\n-}\nmodule Grace.DataF"
  },
  {
    "path": "ghc/Grace/HTTP.hs",
    "chars": 9153,
    "preview": "{-| This module provides a uniform interface for making HTTP requests using both\n    GHC and GHCJS\n-}\nmodule Grace.HTTP\n"
  },
  {
    "path": "ghc/Grace/Width.hs",
    "chars": 632,
    "preview": "{-| This module provides a uniform interface for getting the console width using\n    both GHC and GHCJS\n-}\nmodule Grace."
  },
  {
    "path": "ghcjs/Grace/DataFile.hs",
    "chars": 403,
    "preview": "{-| This module provides a uniform interface for accessing data files using\n    both GHC and GHCJS\n-}\nmodule Grace.DataF"
  },
  {
    "path": "ghcjs/Grace/HTTP.hs",
    "chars": 5411,
    "preview": "{-| This module provides a uniform interface for making HTTP requests using both\n    GHC and GHCJS\n-}\nmodule Grace.HTTP\n"
  },
  {
    "path": "ghcjs/Grace/Size.hs",
    "chars": 150,
    "preview": "module Grace.Width\n    ( -- * Width\n      getWidth\n    ) where\n\n-- | Get the width of the terminal (in columns)\ngetWidth"
  },
  {
    "path": "ghcjs/Grace/Width.hs",
    "chars": 361,
    "preview": "{-| This module provides a uniform interface for getting the console width using\n    both GHC and GHCJS\n-}\nmodule Grace."
  },
  {
    "path": "grace/Main.hs",
    "chars": 75,
    "preview": "module Main where\n\nimport qualified Grace\n\nmain :: IO ()\nmain = Grace.main\n"
  },
  {
    "path": "grace.cabal",
    "chars": 6709,
    "preview": "cabal-version:       >=1.10\nname:                grace\nversion:             1.0.0\nlicense-file:        LICENSE\nauthor:  "
  },
  {
    "path": "prelude/bool/and.ffg",
    "chars": 182,
    "preview": "# Returns `true` if there are no `false`s in the input `List` and `false`\n# otherwise\nlet and\n      : List Bool -> Bool\n"
  },
  {
    "path": "prelude/bool/not.ffg",
    "chars": 66,
    "preview": "# Negate a `Bool`\nlet not (b : Bool) : Bool = b == false\n\nin  not\n"
  },
  {
    "path": "prelude/bool/or.ffg",
    "chars": 180,
    "preview": "# Returns `false` if there are no `true`s in the input `List` and `true`\n# otherwise\nlet or\n      : List Bool -> Bool\n  "
  },
  {
    "path": "prelude/bool/package.ffg",
    "chars": 51,
    "preview": "{ and: ./and.ffg\n, not: ./not.ffg\n, or: ./or.ffg\n}\n"
  },
  {
    "path": "prelude/integer/clamp.ffg",
    "chars": 152,
    "preview": "# Convert an `Integer` to a `Natural` number, clamping negative values to `0`\nlet clamp (n : Integer) : Natural = if n <"
  },
  {
    "path": "prelude/integer/package.ffg",
    "chars": 94,
    "preview": "{ clamp: ./clamp.ffg\n, product: ./product.ffg\n, sum: ./sum.ffg\n, toNatural: ./toNatural.ffg\n}\n"
  },
  {
    "path": "prelude/integer/product.ffg",
    "chars": 156,
    "preview": "# Multiply the elements of a list\nlet product\n      : List Integer -> Integer\n      = fold { cons: \\x y -> x * y : Integ"
  },
  {
    "path": "prelude/integer/sum.ffg",
    "chars": 146,
    "preview": "# Add up the elements of a list\nlet sum\n      : List Integer -> Integer\n      = fold { cons: \\x y -> x + y : Integer, ni"
  },
  {
    "path": "prelude/integer/toNatural.ffg",
    "chars": 203,
    "preview": "# Convert an `Integer` to a `Natural` number, returning `null` if the `Natural`\n# number is negative\nlet toNatural (n : "
  },
  {
    "path": "prelude/list/all.ffg",
    "chars": 273,
    "preview": "# Check if all elements satisfy the given predicate\nlet all\n      : forall (a : Type) . (a -> Bool) -> List a -> Bool\n  "
  },
  {
    "path": "prelude/list/any.ffg",
    "chars": 274,
    "preview": "# Check if any elements satisfy the given predicate\nlet any\n      : forall (a : Type) . (a -> Bool) -> List a -> Bool\n  "
  },
  {
    "path": "prelude/list/concat.ffg",
    "chars": 175,
    "preview": "let concat\n      : forall (a : Type) . List (List a) -> List a\n      = \\lists ->\n          for list of lists\n\n          "
  },
  {
    "path": "prelude/list/generate.ffg",
    "chars": 334,
    "preview": "# Build a `List` by calling the supplied function on all `Natural` numbers from\n# `0` up to but not including the suppli"
  },
  {
    "path": "prelude/list/null.ffg",
    "chars": 156,
    "preview": "# Returns `true` if the `List` is empty and `false` otherwise\nlet null_\n      : forall (a : Type) . List a -> Bool\n     "
  },
  {
    "path": "prelude/list/package.ffg",
    "chars": 180,
    "preview": "{ all: ./all.ffg\n, any: ./any.ffg\n, concat: ./concat.ffg\n, generate: ./generate.ffg\n, null: ./null.ffg\n, reject: ./rejec"
  },
  {
    "path": "prelude/list/reject.ffg",
    "chars": 270,
    "preview": "# Only keep elements of the list where the supplied functions returns `false`\nlet reject\n      : forall (a : Type) . (a "
  },
  {
    "path": "prelude/list/replicate.ffg",
    "chars": 189,
    "preview": "# Create a `List` from `n` copies of an element\nlet replicate\n      : forall (a : Type) . Natural -> a -> List a\n      ="
  },
  {
    "path": "prelude/list/select.ffg",
    "chars": 269,
    "preview": "# Only keep elements of the list where the supplied functions returns `true`\nlet select\n      : forall (a : Type) . (a -"
  },
  {
    "path": "prelude/natural/package.ffg",
    "chars": 44,
    "preview": "{ product: ./product.ffg\n, sum: ./sum.ffg\n}\n"
  },
  {
    "path": "prelude/natural/product.ffg",
    "chars": 156,
    "preview": "# Multiply the elements of a list\nlet product\n      : List Natural -> Natural\n      = fold { cons: \\x y -> x * y : Natur"
  },
  {
    "path": "prelude/natural/sum.ffg",
    "chars": 146,
    "preview": "# Add up the elements of a list\nlet sum\n      : List Natural -> Natural\n      = fold { cons: \\x y -> x + y : Natural, ni"
  },
  {
    "path": "prelude/package.ffg",
    "chars": 176,
    "preview": "{ bool: ./bool/package.ffg\n, real: ./real/package.ffg\n, integer: ./integer/package.ffg\n, list: ./list/package.ffg\n, natu"
  },
  {
    "path": "prelude/real/package.ffg",
    "chars": 44,
    "preview": "{ product: ./product.ffg\n, sum: ./sum.ffg\n}\n"
  },
  {
    "path": "prelude/real/product.ffg",
    "chars": 144,
    "preview": "# Multiply the elements of a list\nlet product\n      : List Real -> Real\n      = fold { cons: \\x y -> x * y : Real, nil: "
  },
  {
    "path": "prelude/real/sum.ffg",
    "chars": 134,
    "preview": "# Add up the elements of a list\nlet sum\n      : List Real -> Real\n      = fold { cons: \\x y -> x + y : Real, nil: 0 : Re"
  },
  {
    "path": "prelude/text/concat.ffg",
    "chars": 141,
    "preview": "# Concatenate a `List` of `Text`\nlet concat\n      : List Text -> Text\n      = fold { cons: \\x -> \\y -> (x + y) : Text, n"
  },
  {
    "path": "prelude/text/concatMap.ffg",
    "chars": 219,
    "preview": "# Transform each value in a `List` into `Text` and concatenate the result\nlet concatMap\n      : forall (a : Type) . (a -"
  },
  {
    "path": "prelude/text/concatMapSep.ffg",
    "chars": 279,
    "preview": "# Concatenate a `List` of `Text` values with a separator in between each value\nlet concatSep = ./concatSep.ffg\n\nlet conc"
  },
  {
    "path": "prelude/text/concatSep.ffg",
    "chars": 606,
    "preview": "# Concatenate a `List` of `Text` values with a separator in between each value\nlet concatSep (separator : Text) (xs : Li"
  },
  {
    "path": "prelude/text/package.ffg",
    "chars": 118,
    "preview": "{ concat: ./concat.ffg\n, concatMap: ./concatMap.ffg\n, concatMapSep: ./concatMapSep.ffg\n, concatSep: ./concatSep.ffg\n}\n"
  },
  {
    "path": "prompts/abnf.md",
    "chars": 12496,
    "preview": "Here's the grammar for Grace if you're not exactly sure what the language does\nand does not permit so that you don't run"
  },
  {
    "path": "prompts/inference.md",
    "chars": 16779,
    "preview": "At [work](https://mercury.com/jobs) I've been researching how to improve the ergonomics of prompt chaining and I wanted "
  },
  {
    "path": "src/Grace/Aeson.hs",
    "chars": 1275,
    "preview": "-- | Utilities for working with the @aeson@ package\nmodule Grace.Aeson where\n\nimport Control.Exception.Safe (Exception(."
  },
  {
    "path": "src/Grace/Compat.hs",
    "chars": 1679,
    "preview": "{-# LANGUAGE CPP #-}\n\n{-# OPTIONS_GHC -Wno-orphans #-}\n\n-- | An internal utility module for backwards compatibility acro"
  },
  {
    "path": "src/Grace/Context.hs",
    "chars": 17633,
    "preview": "{-# LANGUAGE FlexibleContexts #-}\n\n{-| A `Context` is an ordered list of `Entry`s used as the state for the\n    bidirect"
  },
  {
    "path": "src/Grace/Decode.hs",
    "chars": 15215,
    "preview": "{-# LANGUAGE AllowAmbiguousTypes #-}\n{-# LANGUAGE OverloadedLists     #-}\n\n{-| Use this module to decode Grace expressio"
  },
  {
    "path": "src/Grace/Domain.hs",
    "chars": 730,
    "preview": "{-| This module exists primarily to avoid a name clash with constructors of the\n    same name in the \"Grace.Type\" module"
  },
  {
    "path": "src/Grace/Encode.hs",
    "chars": 9336,
    "preview": "{-| Use this module to encode Haskell expressions as Grace expressions\n\n    Example usage:\n\n    >>> encode True\n    Scal"
  },
  {
    "path": "src/Grace/Existential.hs",
    "chars": 1539,
    "preview": "{-| This module provides a newtype wrapper for existential variables used in the\n    type-checking algorithm.\n\n    We do"
  },
  {
    "path": "src/Grace/GitHub.hs",
    "chars": 2127,
    "preview": "-- | Utilities for interacting with GitHub\nmodule Grace.GitHub\n    ( -- * GitHub requests\n      github\n    , GitHub(..)\n"
  },
  {
    "path": "src/Grace/HTTP/Type.hs",
    "chars": 3423,
    "preview": "-- | This module contains types shared between the GHC and GHCJS implementations\nmodule Grace.HTTP.Type where\n\nimport Da"
  },
  {
    "path": "src/Grace/Import.hs",
    "chars": 9079,
    "preview": "-- | This module contains the import resolution logic\nmodule Grace.Import\n    ( -- * Import resolution\n      resolve\n   "
  },
  {
    "path": "src/Grace/Infer.hs",
    "chars": 206388,
    "preview": "{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE RecordWildCards  #-}\n{-# LANGUAGE OverloadedLists  #-}\n\n{-| This module i"
  },
  {
    "path": "src/Grace/Input.hs",
    "chars": 2737,
    "preview": "-- | This module contains the functions and types that power the URI-based imports\nmodule Grace.Input\n    ( -- * Input\n "
  },
  {
    "path": "src/Grace/Interpret.hs",
    "chars": 4714,
    "preview": "{-# LANGUAGE ExistentialQuantification #-}\n{-# LANGUAGE FlexibleContexts          #-}\n{-# LANGUAGE TypeApplications     "
  },
  {
    "path": "src/Grace/Interpret.hs-boot",
    "chars": 500,
    "preview": "{-# LANGUAGE FlexibleContexts #-}\n\nmodule Grace.Interpret where\n\nimport Data.Text (Text)\nimport Grace.Location (Location"
  },
  {
    "path": "src/Grace/Label.hs",
    "chars": 2786,
    "preview": "{-| This module provides label-related logic shared by the parser and\n    pretty-printer\n-}\nmodule Grace.Label\n    ( -- "
  },
  {
    "path": "src/Grace/Location.hs",
    "chars": 2489,
    "preview": "{-| This module contains the `Location` type used for attaching source code to\n    error messages\n-}\nmodule Grace.Locati"
  },
  {
    "path": "src/Grace/Marshal.hs",
    "chars": 10392,
    "preview": "{-# LANGUAGE AllowAmbiguousTypes #-}\n\n-- | Internal module shared between \"Grace.Decode\" and \"Grace.Encode\"\nmodule Grace"
  },
  {
    "path": "src/Grace/Monad.hs",
    "chars": 2194,
    "preview": "{-| This module contains the `Grace` `Monad` shared by type inference and\n    evaluation\n-}\nmodule Grace.Monad\n    ( -- "
  },
  {
    "path": "src/Grace/Monotype.hs",
    "chars": 3968,
    "preview": "{-| This module stores the `Monotype` type representing monomorphic types and\n    utilites for operating on `Monotype`s\n"
  },
  {
    "path": "src/Grace/Normalize.hs",
    "chars": 44440,
    "preview": "{-# LANGUAGE OverloadedLists #-}\n{-# LANGUAGE RecordWildCards #-}\n\n-- | This module contains the logic for efficiently e"
  },
  {
    "path": "src/Grace/Parallelizable.hs",
    "chars": 4081,
    "preview": "-- | Computations with optional concurrency\nmodule Grace.Parallelizable\n    ( -- * Monad\n      Parallelizable(..)\n    , "
  },
  {
    "path": "src/Grace/Parser.hs",
    "chars": 54975,
    "preview": "{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE RecursiveDo      #-}\n\n{-| This module contains the logic for lexing and p"
  },
  {
    "path": "src/Grace/Pretty.hs",
    "chars": 4233,
    "preview": "{-# LANGUAGE FlexibleInstances #-}\n\n-- | Pretty-printing logic\nmodule Grace.Pretty\n    ( -- * Prettyprinting\n      rende"
  },
  {
    "path": "src/Grace/Prompt/Types.hs",
    "chars": 1189,
    "preview": "-- | Types associated with prompting LLMs\nmodule Grace.Prompt.Types\n    ( -- * Types\n      Prompt(..)\n    , Message(..)\n"
  },
  {
    "path": "src/Grace/Prompt.hs",
    "chars": 28115,
    "preview": "{-# LANGUAGE OverloadedLists #-}\n{-# LANGUAGE RecordWildCards #-}\n\n{-# OPTIONS_GHC -fno-warn-orphans #-}\n\n-- | This modu"
  },
  {
    "path": "src/Grace/REPL.hs",
    "chars": 9283,
    "preview": "{-# LANGUAGE FlexibleContexts #-}\n\n-- | This module contains the implementation of the @grace repl@ subcommand\n\nmodule G"
  },
  {
    "path": "src/Grace/Syntax.hs",
    "chars": 72984,
    "preview": "{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE OverloadedLists   #-}\n\n{-| This module contains the syntax tree used for"
  },
  {
    "path": "src/Grace/TH.hs",
    "chars": 3470,
    "preview": "{- | This module provides Template Haskell functions to embed expression and\n     their times at compile-time.\n-}\n\nmodul"
  },
  {
    "path": "src/Grace/Type.hs",
    "chars": 25688,
    "preview": "{-# LANGUAGE FlexibleInstances #-}\n{-# LANGUAGE RecordWildCards   #-}\n\n{-# OPTIONS_GHC -Wno-orphans #-}\n\n{-| This module"
  },
  {
    "path": "src/Grace/Value.hs",
    "chars": 10499,
    "preview": "{-# LANGUAGE OverloadedLists #-}\n\n{-| This module contains the `Value` type used internally for efficient\n    evaluation"
  },
  {
    "path": "src/Grace.hs",
    "chars": 9820,
    "preview": "{-| This module contains the top-level `main` function that implements the\n    command-line API\n-}\nmodule Grace\n    ( --"
  },
  {
    "path": "tasty/Main.hs",
    "chars": 13475,
    "preview": "{-# LANGUAGE BlockArguments        #-}\n{-# LANGUAGE DeriveAnyClass        #-}\n{-# LANGUAGE DeriveGeneric         #-}\n{-#"
  },
  {
    "path": "tasty/data/complex/abstract-fold-input.ffg",
    "chars": 606,
    "preview": "# A `fold` with an abstract record of handlers will default to treating the\n# record as `List` handlers, meaning that th"
  },
  {
    "path": "tasty/data/complex/abstract-fold-output.ffg",
    "chars": 13,
    "preview": "\\x -> fold x\n"
  },
  {
    "path": "tasty/data/complex/abstract-fold-type.ffg",
    "chars": 76,
    "preview": "forall (a : Type) (b : Type) . { nil: a, cons: b -> a -> a } -> List b -> a\n"
  },
  {
    "path": "tasty/data/complex/alternative-input.ffg",
    "chars": 619,
    "preview": "# The purpose of this test is to verify that alternatives bind to their\n# arguments more tightly than function applicati"
  },
  {
    "path": "tasty/data/complex/alternative-output.ffg",
    "chars": 32,
    "preview": "\\f -> f Point{ \"x\": 1, \"y\": 2 }\n"
  },
  {
    "path": "tasty/data/complex/alternative-type.ffg",
    "chars": 104,
    "preview": "forall (a : Type) .\nforall (b : Alternatives) .\n  (< Point: { x: Natural, y: Natural } | b > -> a) -> a\n"
  },
  {
    "path": "tasty/data/complex/arithmetic-input.ffg",
    "chars": 809,
    "preview": "# This test exercises all of the various arithmetic permutations to make sure\n# that they work without type annotations "
  },
  {
    "path": "tasty/data/complex/arithmetic-output.ffg",
    "chars": 826,
    "preview": "{ \"add0\":\n    4\n, \"add1\":\n    -1\n, \"add2\":\n    2.1\n, \"add3\":\n    \"AB\"\n, \"add4\":\n    [ true, false ]\n, \"multiply0\":\n    4"
  },
  {
    "path": "tasty/data/complex/arithmetic-type.ffg",
    "chars": 797,
    "preview": "{ add0:\n    Natural\n, add1:\n    Integer\n, add2:\n    Real\n, add3:\n    Text\n, add4:\n    List Bool\n, multiply0:\n    Natural"
  },
  {
    "path": "tasty/data/complex/chain-identity-input.ffg",
    "chars": 241,
    "preview": "# This test verifies that the type-checker doesn't blow up even when\n# instantiating `id` to a very high-order type\nlet "
  },
  {
    "path": "tasty/data/complex/chain-identity-output.ffg",
    "chars": 5,
    "preview": "true\n"
  },
  {
    "path": "tasty/data/complex/chain-identity-type.ffg",
    "chars": 5,
    "preview": "Bool\n"
  },
  {
    "path": "tasty/data/complex/check-function-input.ffg",
    "chars": 45,
    "preview": "fold { true: 0, false: 1 } : Bool -> Integer\n"
  },
  {
    "path": "tasty/data/complex/check-function-output.ffg",
    "chars": 49,
    "preview": "\\a -> fold { \"true\": 0, \"false\": 1 } a : Integer\n"
  },
  {
    "path": "tasty/data/complex/check-function-type.ffg",
    "chars": 16,
    "preview": "Bool -> Integer\n"
  },
  {
    "path": "tasty/data/complex/church-encode-list-input.ffg",
    "chars": 662,
    "preview": "# This tests that Boehm-Berarducci-encoding a list works correctly\nlet nil : forall (a : Type)\n        . forall (list : "
  },
  {
    "path": "tasty/data/complex/church-encode-list-output.ffg",
    "chars": 6,
    "preview": "false\n"
  },
  {
    "path": "tasty/data/complex/church-encode-list-type.ffg",
    "chars": 5,
    "preview": "Bool\n"
  },
  {
    "path": "tasty/data/complex/default-arguments-input.ffg",
    "chars": 266,
    "preview": "# This test verifies that defaults are correctly selected when the argument is\n# missing, either because it is set to `n"
  },
  {
    "path": "tasty/data/complex/default-arguments-output.ffg",
    "chars": 50,
    "preview": "[ { \"x\": 1, \"y\": false }, { \"x\": 2, \"y\": true } ]\n"
  },
  {
    "path": "tasty/data/complex/default-arguments-type.ffg",
    "chars": 29,
    "preview": "List { x: Natural, y: Bool }\n"
  },
  {
    "path": "tasty/data/complex/destructure-lambda-input.ffg",
    "chars": 136,
    "preview": "# This test exercises all of the supported syntax for destructuring a lambda\n\\{ a, b: Natural, c = 0, d: Natural = 0 } -"
  },
  {
    "path": "tasty/data/complex/destructure-lambda-output.ffg",
    "chars": 41,
    "preview": "\\{ a, b, c = 0, d = 0 } -> a + b + c + d\n"
  },
  {
    "path": "tasty/data/complex/destructure-lambda-type.ffg",
    "chars": 111,
    "preview": "forall (a : Fields) .\n  { a: Natural, b: Natural, c: Optional Natural, d: Optional Natural, a } ->\n    Natural\n"
  },
  {
    "path": "tasty/data/complex/destructure-let-argument-input.ffg",
    "chars": 627,
    "preview": "# This test exercises all of the supported syntax for destructuring a function\n# argument defined using `let`\nlet f { a\n"
  },
  {
    "path": "tasty/data/complex/destructure-let-argument-output.ffg",
    "chars": 73,
    "preview": "{ \"a\": 1, \"b\": 1, \"c\": 1, \"d\": 1, \"e\": null, \"f\": 0, \"g\": null, \"h\": 0 }\n"
  },
  {
    "path": "tasty/data/complex/destructure-let-argument-type.ffg",
    "chars": 204,
    "preview": "forall (a : Type) .\n  { a:\n      Natural\n  , b:\n      Natural\n  , c:\n      Natural\n  , d:\n      Natural\n  , e:\n      Opt"
  },
  {
    "path": "tasty/data/complex/destructure-record-input.ffg",
    "chars": 610,
    "preview": "# This test exercises all of the supported cases for destructuring a record,\n# with or without the field present\nlet { a"
  },
  {
    "path": "tasty/data/complex/destructure-record-output.ffg",
    "chars": 73,
    "preview": "{ \"a\": 1, \"b\": 1, \"c\": 1, \"d\": 1, \"e\": null, \"f\": 0, \"g\": null, \"h\": 0 }\n"
  },
  {
    "path": "tasty/data/complex/destructure-record-type.ffg",
    "chars": 204,
    "preview": "forall (a : Type) .\n  { a:\n      Natural\n  , b:\n      Natural\n  , c:\n      Natural\n  , d:\n      Natural\n  , e:\n      Opt"
  },
  {
    "path": "tasty/data/complex/elaborate-inside-optional-input.ffg",
    "chars": 59,
    "preview": "\\(x : Optional Natural) -> x : Optional (Optional Natural)\n"
  },
  {
    "path": "tasty/data/complex/elaborate-inside-optional-output.ffg",
    "chars": 74,
    "preview": "\\x -> (if let (x : Natural) of x in some x) : Optional (Optional Natural)\n"
  },
  {
    "path": "tasty/data/complex/elaborate-inside-optional-type.ffg",
    "chars": 48,
    "preview": "Optional Natural -> Optional (Optional Natural)\n"
  },
  {
    "path": "tasty/data/complex/equality-input.ffg",
    "chars": 118,
    "preview": "    { number: 0, bool: true, list: [ 2, 3, 5 ], text: \"\" }\n==  { number: 0, bool: true, list: [ 2, 3, 5 ], text: \"\" }\n"
  },
  {
    "path": "tasty/data/complex/equality-output.ffg",
    "chars": 5,
    "preview": "true\n"
  },
  {
    "path": "tasty/data/complex/equality-type.ffg",
    "chars": 5,
    "preview": "Bool\n"
  },
  {
    "path": "tasty/data/complex/even-input.ffg",
    "chars": 262,
    "preview": "# This tests that folding `Natural` numbers doesn't have a gross inefficiency by\n# implementing the even function as a f"
  },
  {
    "path": "tasty/data/complex/even-output.ffg",
    "chars": 5,
    "preview": "true\n"
  },
  {
    "path": "tasty/data/complex/even-type.ffg",
    "chars": 5,
    "preview": "Bool\n"
  },
  {
    "path": "tasty/data/complex/examples-input.ffg",
    "chars": 524,
    "preview": "# These are all of the examples/tutorial from the trygrace.dev tutorial\n{ hello: ../../../examples/tutorial/hello.ffg\n, "
  },
  {
    "path": "tasty/data/complex/examples-output.ffg",
    "chars": 24529,
    "preview": "{ \"hello\":\n    \"Hello, world!\"\n, \"html\":\n    [ true, false, true ]\n, \"data\":\n    { \"An example string\":\n        \"Mercury"
  },
  {
    "path": "tasty/data/complex/examples-type.ffg",
    "chars": 2966,
    "preview": "forall (k : Fields) .\nforall (l : Fields) .\nforall (m : Fields) .\nforall (n : Fields) .\nforall (o : Fields) .\nforall (p "
  },
  {
    "path": "tasty/data/complex/exclaim-input.ffg",
    "chars": 89,
    "preview": "# Test defining and using a function\nlet exclaim t = \"${t}!\"\n\nin  exclaim \"Hello, world\"\n"
  },
  {
    "path": "tasty/data/complex/exclaim-output.ffg",
    "chars": 16,
    "preview": "\"Hello, world!\"\n"
  },
  {
    "path": "tasty/data/complex/exclaim-type.ffg",
    "chars": 5,
    "preview": "Text\n"
  },
  {
    "path": "tasty/data/complex/exhaust-type-variable-alphabet-input.ffg",
    "chars": 326,
    "preview": "# The purpose of this test is to force the type-inference algorithm to use up\n# the type variable alphabet so that we st"
  },
  {
    "path": "tasty/data/complex/exhaust-type-variable-alphabet-output.ffg",
    "chars": 60,
    "preview": "\\x x x x x x x x x x x x x x x x x x x x x x x x x x x -> x\n"
  },
  {
    "path": "tasty/data/complex/exhaust-type-variable-alphabet-type.ffg",
    "chars": 737,
    "preview": "forall (a : Type) .\nforall (b : Type) .\nforall (c : Type) .\nforall (d : Type) .\nforall (e : Type) .\nforall (f : Type) .\n"
  },
  {
    "path": "tasty/data/complex/existential-records-input.ffg",
    "chars": 101,
    "preview": "[ { x: 1, y: true }\n, { x: 1, z: \"\" }\n] : List { x : Natural, y : Optional Bool, z : Optional Text }\n"
  },
  {
    "path": "tasty/data/complex/existential-records-output.ffg",
    "chars": 79,
    "preview": "[ { \"x\": 1, \"y\": some true, \"z\": null }, { \"x\": 1, \"z\": some \"\", \"y\": null } ]\n"
  },
  {
    "path": "tasty/data/complex/existential-records-type.ffg",
    "chars": 56,
    "preview": "List { x: Natural, y: Optional Bool, z: Optional Text }\n"
  },
  {
    "path": "tasty/data/complex/export-show-input.ffg",
    "chars": 581,
    "preview": "{ \"Example 0\":\n    # Expressions should be normalized before they are shown, meaning that the\n    # following expression"
  },
  {
    "path": "tasty/data/complex/export-show-output.ffg",
    "chars": 120,
    "preview": "{ \"Example 0\":\n    \"1\"\n, \"Example 1\":\n    \"\\\\x -> x\"\n, \"Example 2\":\n    \\x -> export show (x : forall (b : Type) . b)\n}\n"
  },
  {
    "path": "tasty/data/complex/export-show-type.ffg",
    "chars": 87,
    "preview": "forall (b : Type) .\n  { \"Example 0\": Text, \"Example 1\": Text, \"Example 2\": b -> Text }\n"
  },
  {
    "path": "tasty/data/complex/extra-fields-input.ffg",
    "chars": 58,
    "preview": "let f (r : { x : Natural }) = r.x\nin  f { x: 1, y: true }\n"
  },
  {
    "path": "tasty/data/complex/extra-fields-output.ffg",
    "chars": 2,
    "preview": "1\n"
  },
  {
    "path": "tasty/data/complex/extra-fields-type.ffg",
    "chars": 8,
    "preview": "Natural\n"
  },
  {
    "path": "tasty/data/complex/fold-bool-input.ffg",
    "chars": 40,
    "preview": "\\{ false, true } -> fold{ false, true }\n"
  },
  {
    "path": "tasty/data/complex/fold-bool-output.ffg",
    "chars": 64,
    "preview": "\\{ false, true } -> fold { \"false\": .'false', \"true\": .'true' }\n"
  },
  {
    "path": "tasty/data/complex/fold-bool-type.ffg",
    "chars": 71,
    "preview": "forall (a : Type) (b : Fields) . { false: a, true: a, b } -> Bool -> a\n"
  },
  {
    "path": "tasty/data/complex/fold-json-input.ffg",
    "chars": 134,
    "preview": "\\{ array, bool, integer, natural, null, object, real, string } ->\n  fold{ array, bool, integer, natural, null, object, r"
  },
  {
    "path": "tasty/data/complex/fold-json-output.ffg",
    "chars": 320,
    "preview": "\\{ array, bool, integer, natural, null, object, real, string } ->\n  fold\n    { \"array\":\n        array\n    , \"bool\":\n    "
  },
  {
    "path": "tasty/data/complex/fold-json-type.ffg",
    "chars": 314,
    "preview": "forall (a : Type) .\nforall (b : Fields) .\n  { array:\n      List a -> a\n  , bool:\n      Bool -> a\n  , integer:\n      Inte"
  },
  {
    "path": "tasty/data/complex/fold-list-input.ffg",
    "chars": 36,
    "preview": "\\{ nil, cons } -> fold{ nil, cons }\n"
  },
  {
    "path": "tasty/data/complex/fold-list-output.ffg",
    "chars": 52,
    "preview": "\\{ nil, cons } -> fold { \"nil\": nil, \"cons\": cons }\n"
  },
  {
    "path": "tasty/data/complex/fold-list-type.ffg",
    "chars": 112,
    "preview": "forall (a : Type) .\nforall (b : Type) .\nforall (c : Fields) .\n  { nil: a, cons: b -> a -> a, c } -> List b -> a\n"
  },
  {
    "path": "tasty/data/complex/fold-missing-field-input.ffg",
    "chars": 1613,
    "preview": "# This tests how `fold` behaves when missing various arguments, along with\n# commentary on why each case behaves the way"
  },
  {
    "path": "tasty/data/complex/fold-missing-field-output.ffg",
    "chars": 304,
    "preview": "{ \"Example 0\":\n    fold { \"false\": some 0, \"true\": null }\n, \"Example 1\":\n    fold { \"succ\": \\x -> x, \"zero\": null }\n, \"E"
  },
  {
    "path": "tasty/data/complex/fold-missing-field-type.ffg",
    "chars": 340,
    "preview": "forall (a : Type) .\nforall (b : Type) .\nforall (c : Type) .\nforall (d : Type) .\n  { \"Example 0\":\n      Bool -> Optional "
  },
  {
    "path": "tasty/data/complex/fold-natural-input.ffg",
    "chars": 38,
    "preview": "\\{ zero, succ } -> fold{ zero, succ }\n"
  },
  {
    "path": "tasty/data/complex/fold-natural-output.ffg",
    "chars": 55,
    "preview": "\\{ zero, succ } -> fold { \"zero\": zero, \"succ\": succ }\n"
  },
  {
    "path": "tasty/data/complex/fold-natural-type.ffg",
    "chars": 78,
    "preview": "forall (a : Type) (b : Fields) . { zero: a, succ: a -> a, b } -> Natural -> a\n"
  },
  {
    "path": "tasty/data/complex/fold-optional-input.ffg",
    "chars": 38,
    "preview": "\\{ null, some } -> fold{ null, some }\n"
  },
  {
    "path": "tasty/data/complex/fold-optional-output.ffg",
    "chars": 61,
    "preview": "\\{ null, some } -> fold { \"null\": .'null', \"some\": .'some' }\n"
  },
  {
    "path": "tasty/data/complex/fold-optional-type.ffg",
    "chars": 112,
    "preview": "forall (a : Type) .\nforall (b : Type) .\nforall (c : Fields) .\n  { null: a, some: b -> a, c } -> Optional b -> a\n"
  },
  {
    "path": "tasty/data/complex/for-annotation-input.ffg",
    "chars": 38,
    "preview": "(for x of [] in x + 1) : List Natural\n"
  },
  {
    "path": "tasty/data/complex/for-annotation-output.ffg",
    "chars": 4,
    "preview": "[ ]\n"
  },
  {
    "path": "tasty/data/complex/for-annotation-type.ffg",
    "chars": 13,
    "preview": "List Natural\n"
  },
  {
    "path": "tasty/data/complex/for-check-list-input.ffg",
    "chars": 101,
    "preview": "# Exercise the check judgment on Optional comprehensions\n(if let x = some 1 in x) : Optional Natural\n"
  },
  {
    "path": "tasty/data/complex/for-check-list-output.ffg",
    "chars": 7,
    "preview": "some 1\n"
  },
  {
    "path": "tasty/data/complex/for-check-list-type.ffg",
    "chars": 17,
    "preview": "Optional Natural\n"
  },
  {
    "path": "tasty/data/complex/for-lambda-input.ffg",
    "chars": 226,
    "preview": "# This test verifies that the `check` rule for `for` assignments works\n# correctly when the type annotation is an unsolv"
  },
  {
    "path": "tasty/data/complex/for-lambda-output.ffg",
    "chars": 26,
    "preview": "\\y -> for x of [ 1 ] in x\n"
  },
  {
    "path": "tasty/data/complex/for-lambda-type.ffg",
    "chars": 38,
    "preview": "forall (a : Type) . a -> List Natural\n"
  },
  {
    "path": "tasty/data/complex/for-list-input.ffg",
    "chars": 149,
    "preview": "let bools = [ false, true ]\n\nfor bool of bools\n\nlet numbers = if bool then [ 1, 2, 3 ] else [ 4, 5, 6 ]\n\nfor number of n"
  },
  {
    "path": "tasty/data/complex/for-list-output.ffg",
    "chars": 197,
    "preview": "[ { \"bool\": false, \"number\": 4 }\n, { \"bool\": false, \"number\": 5 }\n, { \"bool\": false, \"number\": 6 }\n, { \"bool\": true, \"nu"
  },
  {
    "path": "tasty/data/complex/for-list-type.ffg",
    "chars": 37,
    "preview": "List { bool: Bool, number: Natural }\n"
  }
]

// ... and 647 more files (download for full content)

About this extraction

This page contains the full source code of the Gabriel439/grace GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 847 files (897.1 KB), approximately 235.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!