1
1
mirror of https://github.com/anoma/juvix.git synced 2024-11-30 05:42:26 +03:00
Commit Graph

297 Commits

Author SHA1 Message Date
Łukasz Czajka
2e38aa609f
Unify type signature declaration syntax (#3178)
* Closes #2990 
* Allows type signatures for constructors, record fields and axioms to
have the same form as in function definitions, e.g.,
```
field {A} {{M A}} (arg1 arg2 : A) : List A
```
* For constructors, this introduces an ambiguity between record and GADT
syntax with names. For example,
```
ctr {
  A : Type
}
```
can be confused by the parser with initial part of GADT type signature:
```
ctr {A : Type} : A -> C A
```
For now, this is resolved by preferring the record syntax. Hence, it's
currently not possible to use type signatures with implicit arguments
for constructors. Ultimately, the `@` in record syntax should be made
mandatory, which would resolve the ambiguity:
```
ctr@{
  A : Type
}
```

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-11-20 15:57:15 +00:00
Łukasz Czajka
5f803ec6c2
Named arguments for record projections (#3173)
* Closes #3054
* Adds named argument signatures for generated record projections.
2024-11-15 18:25:49 +01:00
Łukasz Czajka
9f25ffde16
Detect termination for nested local definitions (#3169)
* Closes #3147 

When we call a function that is currently being defined (there may be
several such due to nested local definitions), we add a reflexive edge
in the call map instead of adding an edge from the most nested
definition. For example, for

```juvix
go {A B} (f : A -> B) : List A -> List B
  | nil := nil
  | (elem :: next) :=
    let var1 := f elem;
        var2 := go f next;
    in var1 :: var2;
```

we add an edge from `go` to the recursive call `go f next`, instead of
adding an edge from `var2` to `go f next` as before.

This makes the above type-check.

The following still doesn't type-check, because `next'` is not a
subpattern of the clause pattern of `go`. But this is a less pressing
problem.

```juvix
go {A B} (f : A -> B) : List A -> List B
  | nil := nil
  | (elem :: next) :=
    let var1 := f elem;
        var2 (next' : List A) : List B := go f next';
    in myCons var1 (var2 next);
```
2024-11-15 12:30:18 +01:00
Łukasz Czajka
3030196fdd Non-recursive definitions (#3138)
* Closes #2968 
* Implements detection of function-like definitions, which either:
  - have some arguments on the left of `:`, or
  - have at least one clause.
* Only function-like definitions are recursive.
* Non-recursive definitions are not mutually recursive either, and can
be used only after their definition. This necessitates rearranging some
definitions in existing Juvix code.
* Changes the scoping of identifiers in record updates. Now field names
on the right side don't refer to the old values of the record fields but
to identifiers in scope defined outside the record update. To refer to
old values, one needs to explicitly use record projections, e.g.
```
r@Rec{x := Rec.x r}
```
2024-11-04 18:18:39 +01:00
Łukasz Czajka
c143259aee
Allow trailing semicolons everywhere (#3123)
* Closes #3039
* Closes #3043
* Closes #2970
* Closes #3089
* Parser allows trailing semicolons for any kind of semicolon-separated
items:
  - let-block statements,
  - module statements,
  - record declaration statements,
  - record update fields,
  - record pattern fields,
  - named application arguments,
  - list literal items,
  - list pattern items,
  - open statement using/hiding items,
  - `syntax iterator` declaration parameters,
  - `syntax fixity` declaration parameters.
* Formatter prints trailing semicolons if the items are displayed on
separate lines, removes them if on a single line.
* The formatting of multiline lists is changed to make it consistent
with other semicolon-separated blocks:
```
[
  1;
  2;
  3;
]
```
instead of
```
[ 1
; 2
; 3
]
```
2024-10-29 18:25:06 +01:00
Jan Mas Rovira
47696514dc
Set line width to 80 (#3124) 2024-10-25 15:11:19 +02:00
Łukasz Czajka
3cf79faafb
Formatter: add braces when the iterator body is not enclosed (#3122)
* Closes #3091
* Formatter adds braces when the body is not enclosed in braces or
parentheses. Braces-enclosed body is always printed as a block on a new
line:
```
for (acc := 0) (x in lst) {
  x + acc
}
```
* If the body is enclosed in ordinary parentheses, then they are
preserved and the iterator is printed on a single line, if possible:
```
for (acc := 0) (x in lst) (x + acc)
```
This is sometimes useful when you want iterator application as an
argument to something.
2024-10-25 11:42:01 +02:00
Jan Mas Rovira
e951df077d
Don't put a space after the lambda keyword (#3121)
- Closes #3095
2024-10-23 16:02:56 +02:00
Łukasz Czajka
feb422d445
Allow @ in constructor declarations (#3099)
* Closes #3041 
* The old syntax without `@` is still accepted, but the formatter
changes it to the new syntax
2024-10-15 19:15:37 +02:00
Łukasz Czajka
83539148cb
Update standard library coding style according to the guidelines (#3092)
* Closes #3079
* Closes #3086
* Depends on #3088 
* Updates the coding style guidelines (CODING.md) to reflect issues not
foreseen originally
* Changes the unicode arrow printed in the REPL to `->`. This is to make
the output consistent with how function types are written in the
standard library.

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
2024-10-14 15:56:54 +02:00
Jan Mas Rovira
137b6d83eb
Fix termination crash due to empty permutation (#3081)
- Fixes #3064
2024-10-02 18:59:30 +02:00
Jan Mas Rovira
a1926547a2
Reimplement positivity checker (#3057)
- Fixes #3048
- Fixes #3058

Due to #3071 I had to change the order of two lines in
tests/Compilation/positive/test079.juvix.
2024-10-01 13:39:28 +02:00
Jan Mas Rovira
deca981fa3
Ignore files that start with a . (#3072)
- Closes #3068
2024-09-30 12:17:18 +02:00
Paul Cadman
6e58aad8e7
Include the juvix version in the build directory path (#3069)
For example, by default the build directory would now be:

```
.juvix-build/0.6.6
```

It is necessary to separate the build files by compiler version because
the structure of jvo files may be incompatible between compiler
releases.

* Closes https://github.com/anoma/juvix/issues/3019
2024-09-30 08:57:47 +01:00
Jan Mas Rovira
8e204634b8
Fix the location in the parser for .juvix.md (#3020)
This pr makes it possible to properly hihglight .juvix.md files
2024-09-11 14:07:16 +02:00
Jan Mas Rovira
d8919087dd
Fix location of scoped modulePathName (#3011)
Closes #2737.

This issues caused the formatter to sometimes insert unwanted line
breaks.
2024-09-09 15:50:29 +02:00
Łukasz Czajka
ab2d31a313
Compilation of side conditions in pattern matches (#2984)
* Closes #2804 
* Requires #3003
* Front-end syntax for side conditions was implemented in #2852. This PR
implements compilation of side conditions.
* Adds side-conditions to `Match` nodes in Core. Updates Core parsing,
printing and the evaluator.
* Only side-conditions without an `else` branch are allowed in Core. If
there is an `else` branch, the side conditions are translated in
`fromInternal` into nested ifs. Because with `else` the conditions are
exhaustive, there are no implications for pattern exhaustiveness
checking.
* Adjusts the "wildcard row" case in the pattern matching compilation
algorithm to take into account the side conditions.
2024-09-09 12:25:15 +02:00
Jan Mas Rovira
4ae4e4e4d9
Fix a bug that prevented use of name signature defined after the point (#3001)
- Fixes #2999
2024-09-06 14:32:03 +02:00
Jan Mas Rovira
e45503a63e
Fix typechecking of default arguments in signatures with trait arguments (#2998)
- Fixes #2994
2024-09-05 19:43:04 +02:00
Łukasz Czajka
b9d864123a
Isabelle/HOL translation: comments (#2974)
* Closes #2962 
* Depends on #2963 
* In Isabelle/HOL comments cannot appear in internal syntax. All
comments inside a Juvix definition are moved outside: to before the
definition or before the earliest function clause.

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-09-02 15:56:58 +02:00
Łukasz Czajka
a4f3704f4e
Isabelle/HOL translation: records and named patterns (#2963)
* Closes #2894 
* Closes #2895
* The translation of pattern matching on records is a bit tricky because
one cannot pattern match on records in Isabelle, except in top patterns
of function clauses. We thus need to translate into nested pattern
matching and record projections. Named patterns can be translated with a
similar technique and are also handled in this PR.

Checklist
---------
- [x] record creation
- [x] record projections
- [x] record update
- [x] top-level record patterns
- [x] nested record patterns
- [x] named patterns
- [x] remove redundant pattern matching clauses
- [x] remove redundant single-branch pattern matches
2024-08-29 16:15:58 +02:00
Łukasz Czajka
9c980d152a
Translate Judoc comments to Isabelle/HOL (#2958)
* Closes #2891
2024-08-23 20:43:57 +02:00
Łukasz Czajka
4fa5d3ca1b
Isabelle/HOL translation: the isabelle-ignore pragma (#2955)
* Closes #2940
2024-08-19 12:22:10 +02:00
Łukasz Czajka
d60bcccffb
Isabelle/HOL name quoting (#2951)
* Closes #2941 
* Depends on #2950
2024-08-14 15:24:42 +02:00
Łukasz Czajka
52e4e78dc8
Remove unicode from Isabelle/HOL output (#2950)
* Closes #2942
2024-08-13 18:50:25 +02:00
Jan Mas Rovira
bd3b7f1401
Improve css of html documentation and allow different themes (#2931)
1. Refactors css theme to only use variables as source for colors to
make it easier to define themes. In order to define a theme, the
following variables need to be defined (these variables have been taken
mostly from the catppuchin theme):
```
  /* Code */
  --ju-inductive: var(--ctp-green);
  --ju-constructor: var(--ctp-mauve);
  --ju-function: var(--ctp-yellow);
  --ju-module: var(--ctp-lavender);
  --ju-axiom: var(--ctp-red);
  --ju-string: var(--ctp-flamingo);
  --ju-keyword: var(--ctp-sky);
  --ju-delimiter: var(--ctp-overlay2);
  --ju-var: var(--ctp-text);
  --ju-fixity: var(--ctp-sapphire);
  --ju-comment: var(--ctp-rosewater);
  --ju-judoc: var(--ctp-teal);
  --ju-number: var(--ctp-peach);

  /* Text */
  --ju-text: var(--ctp-text);
  --ju-subtext1: var(--ctp-subtext1);
  --ju-subtext0: var(--ctp-subtext0);

  /* Overlay */
  --ju-overlay0: var(--ctp-overlay0);
  --ju-overlay1: var(--ctp-overlay1);
  --ju-overlay2: var(--ctp-overlay2);

  /* Surface */
  --ju-surface0: var(--ctp-surface0);
  --ju-surface1: var(--ctp-surface1);
  --ju-surface2: var(--ctp-surface2);

  /* Panes */
  --ju-base: var(--ctp-base);
  --ju-mantle: var(--ctp-mantle);
  --ju-crust: var(--ctp-mantle);

  /* Theme */
  --ju-main: var(--ctp-maroon);
  --ju-main-link: var(--ctp-maroon);
  --ju-main-link-visited: var(--ctp-flamingo);
  --ju-warning: var(--ctp-red);
```
2. When changing theme, the judoc documentation will also use that
theme, as opposed to only the source code.
3. Added highlighting for module names.
4. When hovering a juvix code element (axiom, constructor, inductive,
etc.), the underline will be of the correct color for the kind. Before
it was always a fixed color.
2024-08-02 16:16:33 +02:00
Paul Cadman
d859a033c0
Add FromNatural trait in package-base (#2926)
This PR adds `FromNatural` to package-base. The change is backwards
compatible for existing Juvix code so we don't need to make a new
version of package-base. The new trait is unused, it will be integrated
in subsequent PRs.

### `FromNatural` trait

The `FromNatural` trait has the following definition.

```
trait
type FromNatural A :=
  mkFromNatural {
    builtin from-nat
    fromNat : Nat -> A
};
```

### `Natural` trait changes

The `Natural` trait is changed to remove its `fromNat` field and add a
new instance field for `FromNatural A`.

### juvix-stdlib changes

`FromNatural` instances are added for `Int` and `Field` in the standard
library.

## Rationale

The `FromNatural` trait will be used for the Bytes type.

We want the following properties for Byte:

1. Values of the Bytes type should be assignable from a non-negative
numeric literal.
2. We don't want to implement + and * for Bytes.

Currently, in order for a type to have property 1. it must have an
instance of `Natural` so property 2. can't be satisfied.

To solve this we split the `from-nat` builtin from the `Natural` trait
into a new trait `FromNatural`.
2024-08-01 08:26:52 +01:00
Jan Mas Rovira
3a9eb20f4a
Fix scanning of names with import prefix (#2929)
1. When the flatparse scanner fails and we fallback to megaparsec, a
warning is issued.
2. The flatparse scanner has been fixed so it is not confused when a
name starts with `import`.
2024-07-31 10:02:38 +02:00
Jonathan Cubides
2416f78a3e
Fix #2924. Use MegaParsec scanner for Markdown files (#2925)
This PR addresses a bug/missing case present since v0.6.2, introduced
specifically by

- PR #2779, 

That PR involves detecting imports in Juvix files before type checking,
and that's the issue.
Detecting/scanning imports is done by running a flat parser (which
ignores the Juvix Markdown structure) and when it fails, it runs a
Megaparser parse. So, for simplicity,
we could just continue using the same Megaparser as before for Juvix
Markdown files.

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-07-30 23:51:55 +02:00
Jan Mas Rovira
178bc5324f
Fix name signature bug and extend test for instance fields (#2928)
- Closes #2923 

This pr fixes a bug where all fields were assigned to be explicit
arguments in the NameSignature Builder. A single line change was enough
to fix it.
```diff
-           RecordStatementField RecordField {..} -> addSymbol @s Explicit Nothing _fieldName _fieldType
+           RecordStatementField RecordField {..} -> addSymbol @s (fromIsImplicitField _fieldIsImplicit) Nothing _fieldName _fieldType
```

I've also added a compilation test for instance fields.
2024-07-30 17:56:42 +02:00
Jan Mas Rovira
1e9850c8f5
Allow instance field declarations (#2916)
- Closes #2897

---------

Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2024-07-29 18:44:16 +02:00
Jan Mas Rovira
a5479d0718
Properly handle confluent imports (#2915)
- Fixes #2914
2024-07-23 19:56:30 +02:00
Jan Mas Rovira
11425aa8e5
Allow record fields to be iterators (#2909)
- Closes #2904
2024-07-22 18:16:06 +02:00
Łukasz Czajka
83837b9c5f
Translate function bodies to Isabelle/HOL (#2868)
* Closes #2813 

Implements a translation from Juvix functions to Isabelle/HOL functions.
This extends the previous Juvix -> Isabelle translation which could
handle only type signatures.

Checklist
---------

- [x] Basic translation
- [x] Polymorphism
- [x] Arithmetic operators
- [x] Numeric literals
- [x] List literals
- [x] Comparison operators
- [x] Boolean operators
- [x] `if` translated to Isabelle `if`
- [x] `true` and `false` translated to Isabelle `True` and `False`
- [x] `zero` and `suc` translated to Isabelle `0` and `Suc`
- [x] `Maybe` translated to Isabelle `option`
- [x] Pairs translated to Isabelle tuples
- [x] Quote Isabelle identifier names (e.g. cannot begin with `_`)
- [x] Rename variables to avoid clashes (in Isabelle/HOL pattern
variables don't shadow function identifiers)
- [x] Common stdlib functions (`map`, `filter`, etc) translated to
corresponding Isabelle functions
- [x] Multiple assignments in a single `let`
- [x] CLI
- [x] Test
- The test is very fragile, similar to the markdown test. It just
compares the result of translation to Isabelle against a predefined
expected output file.

Limitations
-----------

The translation is not designed to be completely correct under all
circumstances. There are aspects of the Juvix language that cannot be
straightforwardly translated to Isabelle/HOL, and these are not planned
to ever be properly handled. There are other aspects that are difficult
but not impossible to translate, and these are left for future work.
Occasionally, the generated code may need manual adjustments to
type-check in Isabelle/HOL.

In particular:
* Higher-rank polymorphism or functions on types cannot be translated as
these features are not supported by Isabelle/HOL. Juvix programs using
these features will not be correctly translated (the generated output
may need manual adjustment).
* In cases where Juvix termination checking diverges from Isabelle/HOL
termination checking, providing a termination proof manually may be
necessary. Non-terminating Juvix functions cannot be automatically
translated and need to be manually modelled in Isabelle/HOL in a
different way (e.g. as relations).
* Comments (including judoc) are ignored. This is left for future work.
* Traits are not translated to Isabelle/HOL type classes / locales. This
is left for future work.
* Mutually recursive functions are not correctly translated. This is
left for future work.
* Record creation, update, field access and pattern matching are not
correctly translated. This is left for future work.
* Named patterns are not correctly translated. This is left for future
work.
* Side conditions in patterns are not supported. This is left for future
work.
* If a Juvix function in the translated module has the same name as some
function from the Isabelle/HOL standard library, there will be a name
clash in the generated code.

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
2024-07-19 08:40:07 +01:00
Jan Mas Rovira
2793514325
Puns for named application (#2890)
Since it is common to want to assign a named argument a variable of the
same name, we add special syntax for it. E.g.
```
f (fieldA : A) (fieldB : B) : S :=
  mkS@{
    fieldC := fieldA; -- normal named argument
    fieldA;  -- pun
    fieldB   -- pun
  };
```
2024-07-16 12:23:22 +02:00
Jan Mas Rovira
3736ed1c2d
Migrate old named application syntax (#2876)
- Closes #2668

This pr migrates the old named application syntax to the new one. In
order to migrate a juvix file to the new syntax it suffices to run the
formatter.
After the next release, we should completely remove the support for the
old syntax.

## Other changes
I've improved Scope negative tests. Previously, when a negative test
failed, you could only see the title of the test and the message
"Incorrect Error", as well as the Haskell file and line where the test
is defined.
This is extremely incovenient because you have to go to the haskell test
file, go to the line where the error is defined, look at the name of the
file and then visit that file. Moreover, you need to manually run the
scoper on that file to see the error that was returned.
I've fixed that and it now shows all relevant information. Example:

![image](https://github.com/anoma/juvix/assets/5511599/f0b7ec60-55dc-4f38-9b51-1fbedbda63f4)
I've implemented this only using the `Generic` instance for the
`ScoperError` type, so doing something similar for the rest of negative
tests should be straightforward.
2024-07-12 18:31:09 +02:00
Paul Cadman
4e227436ce
Make juvix format line width 100 with ribbon width 100 (#2883)
This PR increases the ribbon width of `juvix format` from 60 to 100
characters.

Reasons for the compromise to a fixed 100 chars ribbon width:

* It is clear that the ribbon width of 60 characters was too small.
* A ribbon width of 100 is an acceptable compromise between formatting
code for display and editing code in multiple buffers on the same
screen.
* We would like to avoid making the formatter configurable so that Juvix
code has a consistent look and to save future Juvix users from
discussions about formatting. Maxim: "juvix format's style is no one's
favourite, yet juvix format is everyone's favourite" (thanks go fmt).

## Definition of ribbon width from the
[docs](https://hackage.haskell.org/package/prettyprinter-1.7.1/docs/Prettyprinter.html)

> The page has a certain maximum width, which the layouter tries to not
exceed, by inserting line breaks where possible. The functions given in
this module make it fairly straightforward to specify where, and under
what circumstances, such a line break may be inserted by the layouter,
for example via the
[sep](https://hackage.haskell.org/package/prettyprinter-1.7.1/docs/Prettyprinter.html#v:sep)
function.
> 
> There is also the concept of ribbon width. The ribbon is the part of a
line that is printed, i.e. the line length without the leading
indentation. The layouters take a ribbon fraction argument, which
specifies how much of a line should be filled before trying to break it
up. A ribbon width of 0.5 in a document of width 80 will result in the
layouter to try to not exceed 0.5*80 = 40 (ignoring current indentation
depth).

Examples from
[`anoma-app-patterns:/Token/Transaction.juvix`](8d7e892de3/Token/Transaction.juvix).
NB: The file in the repo is unformatted so will not match the layout in
the left column below.

| Current (line width 150, ribbon width 60) | This PR (line width 100,
ribbon width 100) |
| --- | --- |
| <img width="1000" alt="Screenshot 2024-07-10 at 12 22 46"
src="https://github.com/anoma/juvix/assets/92877/108b59bc-4b3d-4b83-a148-bb7069d7bc13">
| <img width="1000" alt="Screenshot 2024-07-10 at 14 41 33"
src="https://github.com/anoma/juvix/assets/92877/c3cc2c11-bd45-4a07-84ba-3de3d960e542">
|
| <img width="1000" alt="Screenshot 2024-07-10 at 12 23 10"
src="https://github.com/anoma/juvix/assets/92877/9f3e2d47-bcac-409a-8b09-12dde5079ec5">
| <img width="1000" alt="Screenshot 2024-07-10 at 14 42 01"
src="https://github.com/anoma/juvix/assets/92877/3e20db90-5f62-48e0-ac38-ec357d5baec0">
|
| <img width="1000" alt="Screenshot 2024-07-10 at 12 23 21"
src="https://github.com/anoma/juvix/assets/92877/995d01a9-d19d-429e-aee4-114a4a40c899">
| <img width="1075" alt="Screenshot 2024-07-10 at 14 42 14"
src="https://github.com/anoma/juvix/assets/92877/3cfd1663-75d2-48a3-9e93-c7938cc62a47">
|
| <img width="1000" alt="Screenshot 2024-07-10 at 12 23 34"
src="https://github.com/anoma/juvix/assets/92877/1623afe4-89a6-4633-86e0-8d4d39e49e93">
| <img width="1000" alt="Screenshot 2024-07-10 at 14 42 29"
src="https://github.com/anoma/juvix/assets/92877/813f602f-04b4-4ed5-a21e-4435a58d8515">
|
| <img width="1086" alt="Screenshot 2024-07-10 at 12 23 50"
src="https://github.com/anoma/juvix/assets/92877/a04d0664-b9d4-46f3-8ea0-72e5ae0660e1">
| <img width="1093" alt="Screenshot 2024-07-10 at 14 42 40"
src="https://github.com/anoma/juvix/assets/92877/5cf2328d-b911-4ad9-bcc8-3611f4f89465">
|
| <img width="1000" alt="Screenshot 2024-07-10 at 12 24 13"
src="https://github.com/anoma/juvix/assets/92877/53053e7a-32e1-440e-9060-1ab15133a934">
| <img width="1058" alt="Screenshot 2024-07-10 at 14 42 57"
src="https://github.com/anoma/juvix/assets/92877/7263732e-a2cf-43f3-9d49-0599175a160d">
|
2024-07-10 18:21:09 +01:00
Jan Mas Rovira
424ad6e194
Print pipe for else branch in multi if expression (#2881)
- Fixes #2879
2024-07-10 10:39:06 +01:00
Jan Mas Rovira
d08bf942b6
Add front-end support for case expressions boolean side conditions (#2852)
- Syntax for #2804.
- ⚠️ Depends on #2869.

This pr introduces:
1. front-end support (parsing, printing, typechecking) for boolean side
conditions for branches of case expressions.
2. Now `if` is a reserved keyword.
3. Multiway `if` is allowed to have only the `else` branch. I've also
refactored the parser to be simpler.

Example:
```
 multiCaseBr : Nat :=
    case 1 of
      | zero
        | if 0 < 0 := 3
        | else := 4
      | suc (suc n)
        | if 0 < 0 := 3
        | else := n
      | suc n if 0 < 0 := 3;
```
The side if branches must satisfy the following.
1. There must be at least one `if` branch.
4. The `else` branch is optional. If present, it must be the last.

Future work:
1. Translate side if conditions to Core and extend the exhaustiveness
algorithm.
5. Add side if conditions to function clauses.
2024-07-04 01:16:30 +02:00
Łukasz Czajka
7c8016dbca
Pragmas for record fields (#2875)
* Closes #2872
2024-07-03 19:15:35 +02:00
Jan Mas Rovira
82e6b5f5d2
Merge if -> ite renaming from stdlib (#2869) 2024-07-02 10:03:06 +02:00
Paul Cadman
b8cd84170b
Update juvix-stdlib to remove non-ASCII indentifiers (#2857)
This PR updates the juvix-stdlib to the current main commit which
includes:

* https://github.com/anoma/juvix-stdlib/issues/59
* https://github.com/anoma/juvix-stdlib/issues/101

All the Juvix test suite files and examples in this repo have been
updated to be compatible with the new stdlib.
2024-06-26 10:23:35 +02:00
Jan Mas Rovira
e43797f0a0
Generalize import syntax (#2819)
- Closes #2429 

This pr introduces two enchancements to import statements:
1. They can have `using/hiding` list of symbols, with a behaviour
analogous to the open statement.
2. They can be public. When an import is marked as public, a local
module (or a series of nested local modules) is generated like this:
   ```
    import A public;
    -- equivalent to
    import A;
    module A;
      open A public;
    end;
   ```
    It is easier to understand when there is an alias.
    ```
    import A as X.Y public;
    -- equivalent to
    import A;
    module X;
      module Y;
        open A public;
      end;
    end;
   ```
Public imports are allowed to be combined with `using/hiding` modifier
and open statements with the expected behaviour.
2024-06-21 15:02:30 +02:00
Jan Mas Rovira
44cdd8404b
Fix generation of wildcards in RecordPattern (#2802)
- Closes #2801
2024-06-04 18:28:25 +02:00
Jan Mas Rovira
e9afdad82a
Parallel pipeline (#2779)
This pr introduces parallelism in the pipeline to gain performance. I've
included benchmarks at the end.

- Closes #2750.

# Flags:
There are two new global flags:
1. `-N / --threads`. It is used to set the number of capabilities.
According to [GHC
documentation](https://hackage.haskell.org/package/base-4.20.0.0/docs/GHC-Conc.html#v:setNumCapabilities):
_Set the number of Haskell threads that can run truly simultaneously (on
separate physical processors) at any given time_. When compiling in
parallel, we create this many worker threads. The default value is `-N
auto`, which sets `-N` to half the number of logical cores, capped at 8.
2. `--dev-show-thread-ids`. When given, the thread id is printed in the
compilation progress log. E.g.

![image](https://github.com/anoma/juvix/assets/5511599/9359fae2-0be1-43e5-8d74-faa82cba4034)

# Parallel compilation
1. I've added `src/Parallel/ParallelTemplate.hs` which contains all the
concurrency related code. I think it is good to keep this code separated
from the actual compiler code.
2. I've added a progress log (only for the parallel driver) that outputs
a log of the compilation progress, similar to what stack/cabal do.

# Code changes:
1. I've removed the `setup` stage where we were registering
dependencies. Instead, the dependencies are registered when the
`pathResolver` is run for the first time. This way it is safer.
1. Now the `ImportTree` is needed to run the pipeline. Cycles are
detected during the construction of this tree, so I've removed `Reader
ImportParents` from the pipeline.
3. For the package pathresolver, we do not support parallelism yet (we
could add support for it in the future, but the gains will be small).
4. When `-N1`, the pipeline remains unchanged, so performance should be
the same as in the main branch (except there is a small performance
degradation due to adding the `-threaded` flag).
5. I've introduced `PipelineOptions`, which are options that are used to
pass options to the effects in the pipeline.
6. `PathResolver` constraint has been removed from the `upTo*` functions
in the pipeline due to being redundant.
7. I've added a lot of `NFData` instances. They are needed to force the
full evaluation of `Stored.ModuleInfo` in each of the threads.
2. The `Cache` effect uses
[`SharedState`](https://hackage.haskell.org/package/effectful-core-2.3.0.1/docs/Effectful-State-Static-Shared.html)
as opposed to
[`LocalState`](https://hackage.haskell.org/package/effectful-core-2.3.0.1/docs/Effectful-Writer-Static-Local.html).
Perhaps we should provide different versions.
3. I've added a `Cache` handler that accepts a setup function. The setup
is triggered when a miss is detected. It is used to lazily compile the
modules in parallel.

# Tests
1. I've adapted the smoke test suite to ignore the progress log in the
stderr.
5. I've had to adapt `tests/positive/Internal/Lambda.juvix`. Due to
laziness, a crash happening in this file was not being caught. The
problem is that in this file we have a lambda function with different
number of patterns in their clauses, which we currently do not support
(https://github.com/anoma/juvix/issues/1706).
6. I've had to comment out the definition
   ```
   x : Box ((A : Type) → A → A) := box λ {A a := a};
   ```
From the test as it was causing a crash
(https://github.com/anoma/juvix/issues/2247).
# Future Work
1. It should be investigated how much performance we lose by fully
evaluating the `Stored.ModuleInfo`, since some information in it will be
discarded. It may be possible to be more fine-grained when forcing
evaluation.
8. The scanning of imports to build the import tree is sequential. Now,
we build the import tree from the entry point module and only the
modules that are imported from it are in the tree. However, we have
discussed that at some point we should make a distinction between
`juvix` _the compiler_ and `juvix` _the build tool_. When using `juvix`
as a build tool it makes sense to typecheck/compile (to stored core) all
modules in the project. When/if we do this, scanning imports in all
modules in parallel becomes trivial.
9. The implementation of the `ParallelTemplate` uses low level
primitives such as
[forkIO](https://hackage.haskell.org/package/base-4.20.0.0/docs/Control-Concurrent.html#v:forkIO).
At some point it should be refactored to use safer functions from the
[`Effectful.Concurrent.Async`](https://hackage.haskell.org/package/effectful-2.3.0.0/docs/Effectful-Concurrent-Async.html)
module.
10. The number of cores and worker threads that we spawn is determined
by the command line. Ideally, we could use to import tree to compute an
upper bound to the ideal number of cores to use.
11. We could add an animation that displays which modules are being
compiled in parallel and which have finished being compiled.

# Benchmarks

On some benchmarks, I include the GHC runtime option
[`-A`](https://downloads.haskell.org/ghc/latest/docs/users_guide/runtime_control.html#rts-flag--A%20%E2%9F%A8size%E2%9F%A9),
which sometimes makes a good impact on performance. Thanks to
@paulcadman for pointing this out. I've figured a good combination of
`-N` and `-A` through trial and error (but this oviously depends on the
cpu and juvix projects).

## Typecheck the standard library
   
### Clean run (88% faster than main):
```
 hyperfine --warmup 1 --prepare 'juvix clean' 'juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432'  'juvix -N 4 typecheck Stdlib/Prelude.juvix' 'juvix-main typecheck Stdlib/Prelude.juvix'
Benchmark 1: juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432
  Time (mean ± σ):     444.1 ms ±   6.5 ms    [User: 1018.0 ms, System: 77.7 ms]
  Range (min … max):   432.6 ms … 455.9 ms    10 runs

Benchmark 2: juvix -N 4 typecheck Stdlib/Prelude.juvix
  Time (mean ± σ):     628.3 ms ±  23.9 ms    [User: 1227.6 ms, System: 69.5 ms]
  Range (min … max):   584.7 ms … 670.6 ms    10 runs

Benchmark 3: juvix-main typecheck Stdlib/Prelude.juvix
  Time (mean ± σ):     835.9 ms ±  12.3 ms    [User: 788.5 ms, System: 31.9 ms]
  Range (min … max):   816.0 ms … 853.6 ms    10 runs

Summary
  juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432 ran
    1.41 ± 0.06 times faster than juvix -N 4 typecheck Stdlib/Prelude.juvix
    1.88 ± 0.04 times faster than juvix-main typecheck Stdlib/Prelude.juvix
```
   
### Cached run (43% faster than main):
```
hyperfine --warmup 1 'juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432'  'juvix -N 4 typecheck Stdlib/Prelude.juvix' 'juvix-main typecheck Stdlib/Prelude.juvix'
Benchmark 1: juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432
  Time (mean ± σ):     241.3 ms ±   7.3 ms    [User: 538.6 ms, System: 101.3 ms]
  Range (min … max):   231.5 ms … 251.3 ms    11 runs

Benchmark 2: juvix -N 4 typecheck Stdlib/Prelude.juvix
  Time (mean ± σ):     235.1 ms ±  12.0 ms    [User: 405.3 ms, System: 87.7 ms]
  Range (min … max):   216.1 ms … 253.1 ms    12 runs

Benchmark 3: juvix-main typecheck Stdlib/Prelude.juvix
  Time (mean ± σ):     336.7 ms ±  13.3 ms    [User: 269.5 ms, System: 67.1 ms]
  Range (min … max):   316.9 ms … 351.8 ms    10 runs

Summary
  juvix -N 4 typecheck Stdlib/Prelude.juvix ran
    1.03 ± 0.06 times faster than juvix -N 4 typecheck Stdlib/Prelude.juvix +RTS -A33554432
    1.43 ± 0.09 times faster than juvix-main typecheck Stdlib/Prelude.juvix
```
## Typecheck the test suite of the containers library
At the moment this is the biggest juvix project that we have.

### Clean run (105% faster than main)
```
hyperfine --warmup 1 --prepare 'juvix clean' 'juvix -N 6 typecheck Main.juvix +RTS -A67108864' 'juvix -N 4 typecheck Main.juvix' 'juvix-main typecheck Main.juvix'
Benchmark 1: juvix -N 6 typecheck Main.juvix +RTS -A67108864
  Time (mean ± σ):      1.006 s ±  0.011 s    [User: 2.171 s, System: 0.162 s]
  Range (min … max):    0.991 s …  1.023 s    10 runs

Benchmark 2: juvix -N 4 typecheck Main.juvix
  Time (mean ± σ):      1.584 s ±  0.046 s    [User: 2.934 s, System: 0.149 s]
  Range (min … max):    1.535 s …  1.660 s    10 runs

Benchmark 3: juvix-main typecheck Main.juvix
  Time (mean ± σ):      2.066 s ±  0.010 s    [User: 1.939 s, System: 0.089 s]
  Range (min … max):    2.048 s …  2.077 s    10 runs

Summary
  juvix -N 6 typecheck Main.juvix +RTS -A67108864 ran
    1.57 ± 0.05 times faster than juvix -N 4 typecheck Main.juvix
    2.05 ± 0.03 times faster than juvix-main typecheck Main.juvix
```

### Cached run (54% faster than main)
```
hyperfine --warmup 1 'juvix -N 6 typecheck Main.juvix +RTS -A33554432'  'juvix -N 4 typecheck Main.juvix' 'juvix-main typecheck Main.juvix'
Benchmark 1: juvix -N 6 typecheck Main.juvix +RTS -A33554432
  Time (mean ± σ):     551.8 ms ±  13.2 ms    [User: 1419.8 ms, System: 199.4 ms]
  Range (min … max):   535.2 ms … 570.6 ms    10 runs

Benchmark 2: juvix -N 4 typecheck Main.juvix
  Time (mean ± σ):     636.7 ms ±  17.3 ms    [User: 1006.3 ms, System: 196.3 ms]
  Range (min … max):   601.6 ms … 655.3 ms    10 runs

Benchmark 3: juvix-main typecheck Main.juvix
  Time (mean ± σ):     847.2 ms ±  58.9 ms    [User: 710.1 ms, System: 126.5 ms]
  Range (min … max):   731.1 ms … 890.0 ms    10 runs

Summary
  juvix -N 6 typecheck Main.juvix +RTS -A33554432 ran
    1.15 ± 0.04 times faster than juvix -N 4 typecheck Main.juvix
    1.54 ± 0.11 times faster than juvix-main typecheck Main.juvix
```
2024-05-31 12:41:30 +01:00
Łukasz Czajka
161a34c36b
Optional braces in case syntax (#2778)
* Closes #2769 
* Removes old case syntax
* Pretty printing doesn't print braces in `case` if the `case` is a
"top" expression in a definition.
2024-05-22 18:14:03 +01:00
Łukasz Czajka
d59d02ccb7
Bugfix: modules associated with inductive types should be declared after their inductive types (#2768)
* Closes #2763. 
* Fixes a bug in the scoper, likely introduced in
https://github.com/anoma/juvix/pull/2468 by making later declarations
depend on earlier ones. The problem was that the inductive modules were
always added at the beginning of a section, which resulted in an
incorrect definition dependency graph (an inductive type depended on its
associated projections).
* Now inductive modules are added just after a group of inductive
definitions, before the next function definition. This implies that
inductive type definitions which depend on each other cannot be
separated by function definitions. Existing Juvix code needs to be
adjusted.
* The behaviour is now equivalent to "manually" inserting module
declarations with projections after each group of inductive definitions.
2024-05-14 19:32:22 +02:00
Jan Mas Rovira
8497c29982
Add integer literals with bases (#2746)
- Closes #2735 

Allow integer literals to be expressed in different bases:
1. binary, with prefix `0b`.
2. octal, with prefix `0o`.
3. decimal, with no prefix.
4. hexadecimal, with prefix `0x`. Both capital and lower case letters
are parsed. They are always printed as lower case letters.

This applies to all languages that use integer literals, but only in the
concrete language the base will be preserved when pretty printing.
2024-04-22 17:07:32 +02:00
Paul Cadman
52a3eed162
Support extract-module-statements attribute in juvix code blocks (#2734)
This PR adds support for the `extract-module-statements` attribute for
Juvix code blocks:

So if you write something like the following block in a Juvix markdown
file:

````
```juvix extract-module-statements
module Foo;
   type T := t;
end;
```
````

The statement `type T := t;` from the body of the module is rendered in
the output. The `module Foo;` , and `end;` lines are not rendered in the
output.

A block with the `extract-module-statements` must contain a single local
module statement and nothing else. An error is reported if this is not
the case.

The `extract-module-statements` attribute also takes an optional
argument. It sets the number of statements from the module body to drop
from the output.

In the following example, the output will contain the single line `a : T
:= t;`.

````
```juvix extract-module-statements 1
module Foo;
   type T := t;
   a : T  := t;
end;
```
````

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-04-18 17:01:59 +01:00
Paul Cadman
346b18fd67
Use the target of aliases when resolving name signatures and iterators (#2669)
* Closes https://github.com/anoma/juvix/issues/2664

As well as this fix we rename lens scopedIdenName to scopedIdenSrcName.
`scopedIdenSrcName` refers to the name of an identifier from the source
code. The name `scopedIdenName` is confusing because users of
`ScopedIden` may think that this lens refers to the only name associated
with `ScopedIden`, they may want `scopedIdenNameFinal` instead.
2024-02-26 11:53:47 +00:00