Skip to content

Commit

Permalink
Autoscoped constructors (#9190)
Browse files Browse the repository at this point in the history
Fixes #8645 by recognizing `~` prefix to constructor names.
  • Loading branch information
JaroslavTulach authored Mar 4, 2024
1 parent 3be5e58 commit 5676618
Show file tree
Hide file tree
Showing 31 changed files with 763 additions and 332 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@
- [Add run_google_report method][8907]
- [Execute and debug individual Enso files in VSCode extension][8923]
- [Check type of `self` when calling a method using the static syntax][8867]
- [Autoscoped constructors][9190]

[3227]: https://github.com/enso-org/enso/pull/3227
[3248]: https://github.com/enso-org/enso/pull/3248
Expand Down Expand Up @@ -1217,6 +1218,7 @@
[8907]: https://github.com/enso-org/enso/pull/8907
[8923]: https://github.com/enso-org/enso/pull/8923
[8867]: https://github.com/enso-org/enso/pull/8867
[9190]: https://github.com/enso-org/enso/pull/9190

# Enso 2.0.0-alpha.18 (2021-10-12)

Expand Down
10 changes: 9 additions & 1 deletion distribution/lib/Standard/Base/0.0.0-dev/src/Errors/Common.enso
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,15 @@ type Type_Error
_ -> ". Try to apply " + (missing_args.join ", ") + " arguments"
_ -> tpe.to_text

"Type error: expected "+self.comment+" to be "+self.expected.to_display_text+", but got "+(if type_of_actual.is_a Text then type_of_actual else "<ERR>")+"."
got = if type_of_actual.is_a Text then type_of_actual else "<ERR>"
exp = self.expected.to_display_text
msg = self.comment
. replace "{exp}" exp
. replace "{got}" got
"Type error: "+msg+"."

to_text : Text
to_text self = self.to_display_text

@Builtin_Type
type Compile_Error
Expand Down
68 changes: 7 additions & 61 deletions docs/runtime-roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,11 @@ priority, but the dependencies between tasks are described.

## Technology Choices

With the advent of Java 17 and its ergonomic improvements (read:
pattern-matching), it makes little sense to retain the usage of Scala throughout
the compiler. The language was originally introduced due to the capabilities of
its type system in comparison to Java's, but very little of this functionality
has been used in the end.

We recommend moving everything to Java as part of this work, as you will end up
with better tooling support. Scala has been a problem child.

Enso originally started working with Java 8, and was transitioned (painfully,
due to the JPMS) to Java 11. Java 8 was EOL'd by the graal team after a couple
of years. It seems likely that Java 11 will suffer a similar fate, though the
transition from 11 to 17 will be far less painful as it doesn't introduce any
breaking language-level changes.
Enso interpreter is written in a mixture of Scala and Java. Scala was originally
used due to the capabilities of its type system in comparison to Java's. Modern
Java (as provided by JDK 21 or [Frgaal compiler](http://frgaal.org)) meets most
of the needs too. The ultimate goal is to write everything in Java and also keep
up with most recent long term supported JDK/GraalVM releases.

## Static Analysis

Expand All @@ -49,7 +40,8 @@ Currently, the IR is:
- Very verbose and difficult to add a new node to. Adding a new node requires
adding ~100 lines of code that could likely be automated away. Lots of
boilerplate.
- Of unknown performance.
- Of poor performance as witnessed by
[static compiler benchmarks](https://github.com/enso-org/enso/pull/9158)
- Partially mutable, making it confusing as to which things are shared.

A new IR for Enso would have to:
Expand Down Expand Up @@ -252,25 +244,6 @@ To rectify this situation, we recommend implementing a system we have termed
With this done, it may still be necessary to create a Java DSL for implementing
built-in methods and types, but that is unclear at this point.

### Static Methods on Types

Currently, Enso allows calling methods on _modules_, _constructors_, and
_instances_. This does not conform to the language specification because it
allows constructors and instances to be treated the same at runtime. This leads
to odd results (see the ticket below).

The end result should be compliant with the design described
[here](https://github.com/enso-org/enso/issues/1851), and needs to be taken into
account when defining builtins.

### Better Safepointing

Enso currently uses a hand-rolled safepointing system for interrupting threads
and handling resource finalisation. With 21.1, Truffle landed its own system for
doing this. Enso should be updated to use
[the new system](https://github.com/oracle/graal/blob/master/truffle/docs/Safepoints.md),
instead, as it will provide better performance and more robust operation.

## Runtime Performance

While Enso is performant when it gets JITted by GraalVM, the performance when
Expand All @@ -290,29 +263,6 @@ This can be greatly improved.
be improved.
- Many of the above-listed static optimisations will greatly help here.

### Unboxed Atoms

Currently every atom in Enso is stored boxed. In limited circumstances it may be
possible to unbox these and hence remove the indirection cost when accessing
their data.

- Read the details of Truffle's
[`DynamicObject`](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/object/DynamicObject.html),
and the sources.
- Use this system to inform the design for a system that reduces the overhead of
dynamic field names and arities when accessing data on Atoms.

### Unboxed Vectors

Enso currently doesn't have support for unboxed arrays (and hence vectors). This
means that it incurs a significant performance cost when working with pure
numerical arrays. This can be improved.

- Read the truffle documentation on
[truffle libraries](https://github.com/oracle/graal/blob/master/truffle/docs/TruffleLibraries.md).
- Based on this, define a system that seamlessly specializes and deoptimises
between boxed and unboxed arrays as necessary.

## IDE

As Enso's primary mode of use is in the IDE, there are a number of important
Expand Down Expand Up @@ -362,7 +312,3 @@ preprocessors.
- Implement caching support for the visualization expression processing.
- This cache should, much like the IDE's introspection cache, track and save the
values of all top-level bindings in the visualization preprocessor.

## Parser

Parser
12 changes: 6 additions & 6 deletions docs/syntax/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ binds the function name. This means that:
user-defined type for the function.

```ruby
sum : (a: Monoid) -> a -> a
sum : (a:Monoid) -> a -> a
sum : x -> y -> x + y
sum x y = x + y
```
Expand All @@ -113,19 +113,19 @@ Methods can be defined in Enso in two ways:
```ruby
type Maybe a
Nothing
type Just (value : a)
Just (value : a)

isJust = case this of
Nothing -> False
Just _ -> True
is_just self = case self of
Maybe.Nothing -> False
Maybe.Just _ -> True
```

2. **As an Extension Method:** A function defined _explicitly_ on an atom counts
as an extension method on that atom. It can be defined on a typeset to apply
to all the atoms within that typeset.

```ruby
Number.floor = case this of
Number.floor self = case self of
Integer -> ...
...
```
Expand Down
48 changes: 32 additions & 16 deletions docs/syntax/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,32 +276,48 @@ context-dependent manner that is discussed properly in the
[type system design document](../types/README.md), but is summarised briefly
below.

- **Name and Fields:** When you provide the keyword with only a name and some
field names, this creates an atom.

```ruby
type Just value
```

- **Body with Atom Definitions:** If you provide a body with atom definitions,
this defines a smart constructor that defines the atoms and related functions
by returning a typeset.

```ruby
type Maybe a
type Maybe
Nothing
type Just (value : a)
Just (value : Integer)
is_just self = case self of
Maybe.Nothing -> False
Maybe.Just _ -> True
nothing self = self.is_just.not
```

isJust = case this of
Nothing -> False
Just _ -> True
To reference atoms use type name followed by the name of the atom. E.g.
`Maybe.Nothing` or `Maybe.Just 2`. Atom constructors act like functions and
fully support currying - e.g. one can create `fn = Maybe.Just` and later apply
two to it (`fn 2`) to obtain new atom.

nothing = not isJust
- **Autoscoped Constructors:** Referencing constructors via their type name may
lead to long and boilerplate code. To simplify referencing constructors when
the _context is known_ a special `~` syntax is supported. Should there be a
method `describe`:

```ruby
describe (m : Maybe) -> Text = if m.is_just then m.value.to_text else "Empty"
```

one may invoke it as `describe (Maybe.Just 5)` - e.g. the regular way. Or one
may use _autoscoped constructors_ and call

```ruby
describe (~Just 5)
```

Please note that the `type Foo (a : t)` is syntax only allowable inside a type
definition. It defines an atom `Foo`, but constrains the type variable of the
atom _in this usage_.
the argument `(~Just 5)` is _recorded but not executed_ until it is send to
the `describe` method. The argument of the `describe` method is known to be of
type `Maybe` and have `Just` constructor. The _scope_ is now known and the so
far deferred `~` value gets evaluated. `Maybe.Just 5` atom is constructed and
execution of `describe` method continues with such atom.

- **Body Without Atom Definitions:** If you provide a body and do not define any
atoms within it, this creates an interface that asserts no atoms as part of
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package org.enso.interpreter.bench.benchmarks.semantic;

import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.logging.Level;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.io.IOAccess;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.BenchmarkParams;
import org.openjdk.jmh.infra.Blackhole;

Expand All @@ -33,16 +36,7 @@ public class ArrayProxyBenchmarks {

@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
Engine eng =
Engine.newBuilder()
.allowExperimentalOptions(true)
.option(RuntimeOptions.LOG_LEVEL, Level.WARNING.getName())
.logHandler(System.err)
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath())
.build();
var ctx = Context.newBuilder().engine(eng).allowIO(IOAccess.ALL).allowAllAccess(true).build();
var ctx = SrcUtil.newContextBuilder().build();
var code =
"""
import Standard.Base.Data.Vector.Vector
Expand Down
Loading

0 comments on commit 5676618

Please sign in to comment.