Skip to content

Commit

Permalink
Removing Unsafe.set_atom_field (#4023)
Browse files Browse the repository at this point in the history
Introducing `Meta.atom_with_hole` to create an `Atom` _with a hole_ that is then _safely_ filled in later.
  • Loading branch information
JaroslavTulach authored Jan 9, 2023
1 parent 3379ce5 commit 41b2aac
Show file tree
Hide file tree
Showing 14 changed files with 768 additions and 286 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@
- [IGV can jump to JMH sources & more][4008]
- [Basic support of VSCode integration][4014]
- [Sync language server with file system after VCS restore][4020]
- [Introducing Meta.atom_with_hole][4023]
- [Report failures in name resolution in type signatures][4030]

[3227]: https://github.com/enso-org/enso/pull/3227
Expand Down Expand Up @@ -580,6 +581,7 @@
[4008]: https://github.com/enso-org/enso/pull/4008
[4014]: https://github.com/enso-org/enso/pull/4014
[4020]: https://github.com/enso-org/enso/pull/4020
[4023]: https://github.com/enso-org/enso/pull/4023
[4030]: https://github.com/enso-org/enso/pull/4030

# Enso 2.0.0-alpha.18 (2021-10-12)
Expand Down
90 changes: 56 additions & 34 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/List.enso
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import project.Data.Vector.Vector
import project.Error.Error
import project.Function.Function
import project.Nothing.Nothing
import project.Runtime.Unsafe
import project.Meta

from project.Data.Boolean import Boolean, True, False

Expand Down Expand Up @@ -185,13 +185,19 @@ type List
filter : (Filter_Condition | (Any -> Boolean)) -> Vector Any
filter self filter = case filter of
_ : Filter_Condition -> self.filter filter.to_predicate
predicate : Function ->
go_filter list = case list of
Nil -> Nil
Cons h t ->
rest = go_filter t
if predicate h then Cons h rest else rest
go_filter self
predicate : Function -> case self of
Nil -> Nil
Cons x xs -> if predicate x . not then @Tail_Call xs.filter filter else
go list fill = case list of
Nil -> fill Nil
Cons h t -> if predicate h . not then @Tail_Call go t fill else
res = Meta.atom_with_hole (Cons h _)
fill res.value
@Tail_Call go t res.fill

res = Meta.atom_with_hole (Cons x _)
go xs res.fill
res.value

## Applies a function to each element of the list, returning the list of
results.
Expand All @@ -209,9 +215,18 @@ type List
map self f = case self of
Nil -> Nil
Cons h t ->
res = Cons (f h) Nil
map_helper t res f
res
go : List -> Any -> (Any -> Any) -> Nothing
go list fill = case list of
Nil -> fill Nil
Cons h t ->
v = f h
res = Meta.atom_with_hole (Cons v _)
fill res.value
@Tail_Call go t res.fill
v = f h
res = Meta.atom_with_hole (Cons v _)
go t res.fill
res.value

## Applies a function to each element of the list.

Expand Down Expand Up @@ -281,7 +296,16 @@ type List
take_start : Integer -> List
take_start self count = if count <= 0 then Nil else case self of
Nil -> Nil
Cons a b -> Cons a (b.take_start count-1)
Cons a b ->
go c l fill = if c <= 0 then fill Nil else case l of
Nil -> fill Nil
Cons a b ->
res = Meta.atom_with_hole (Cons a _)
fill res.value
@Tail_Call go c-1 b res.fill
res = Meta.atom_with_hole (Cons a _)
go count-1 b res.fill
res.value

## Get the first element from the list.

Expand Down Expand Up @@ -319,12 +343,21 @@ type List
example_init = Examples.list.init
init : List ! Empty_Error
init self =
init_fn x y = case y of
Nil -> Nil
Cons a b -> Cons x (init_fn a b)
case self of
Nil -> Error.throw Empty_Error
Cons a b -> init_fn a b
Cons _ Nil -> Nil
Cons a b ->
go l fill = case l of
Cons x xs -> case xs of
Nil -> fill Nil
Cons _ _ ->
res = Meta.atom_with_hole (Cons x _)
fill res.value
@Tail_Call go xs res.fill

res = Meta.atom_with_hole (Cons a _)
go b res.fill
res.value

## Get the last element of the list.

Expand Down Expand Up @@ -369,6 +402,13 @@ type List
builder.append elem
builder.to_vector

to_text : Text
to_text self =
go l t = case l of
Nil -> t + "Nil"
Cons x xs -> @Tail_Call go xs (t + "Cons " + x.to_text + " ")
go self ""

## UNSTABLE

An error representing that the list is empty.
Expand All @@ -379,21 +419,3 @@ type Empty_Error
to_display_text : Text
to_display_text self = "The List is empty."

## PRIVATE
A helper for the `map` function.

Arguments:
- list: The list to map over.
- cons: The current field to set.
- f: The function to apply to the value.

Uses unsafe field mutation under the hood, to rewrite `map` in
a tail-recursive manner. The mutation is purely internal and does not leak
to the user-facing API.
map_helper : List -> Any -> (Any -> Any) -> Nothing
map_helper list cons f = case list of
Nil -> Unsafe.set_atom_field cons 1 Nil
Cons h t ->
res = Cons (f h) Nil
Unsafe.set_atom_field cons 1 res
@Tail_Call map_helper t res f
11 changes: 11 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,17 @@ get_constructor_name atom_constructor = @Builtin_Method "Meta.get_constructor_na
new_atom : Any -> Array -> Atom
new_atom constructor fields = @Builtin_Method "Meta.new_atom"

## PRIVATE

Constructs a new atom with a "hole". Returns an object with `value` and
`fill` properties. Value contains the created atom and `fill` holds a
function to "fill the hole" later.

Arguments:
- factory: a function that takes the "hole" element and returns newly created atom
atom_with_hole : (Any -> Atom) -> Any
atom_with_hole factory = @Builtin_Method "Meta.atom_with_hole_builtin"

## UNSTABLE
ADVANCED

Expand Down
18 changes: 0 additions & 18 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Runtime/Unsafe.enso

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package org.enso.interpreter.bench.benchmarks.semantic;

import java.io.ByteArrayOutputStream;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
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;


@BenchmarkMode(Mode.AverageTime)
@Fork(1)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class ListBenchmarks {
private final int LENGTH_OF_EXPERIMENT = 1_000_000;
private Value list;
private Value plusOne;
private Value self;
private Value sum;
private Value oldSum;

@Setup
public void initializeBenchmark(BenchmarkParams params) throws Exception {
var ctx = Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(true)
.allowAllAccess(true)
.logHandler(new ByteArrayOutputStream())
.option(
"enso.languageHomeOverride",
Paths.get("../../distribution/component").toFile().getAbsolutePath()
).build();

var benchmarkName = params.getBenchmark().replaceFirst(".*\\.", "");
var code = """
from Standard.Base.Data.List.List import Cons, Nil
import Standard.Base.IO
plus_one list = list.map (x -> x + 1)
sum list acc =
case list of
Nil -> acc
Cons x xs -> @Tail_Call sum xs acc+x
generator n =
go x v l = if x > n then l else
@Tail_Call go x+1 v+1 (Cons v l)
go 1 1 Nil
""";

var module = ctx.eval(SrcUtil.source(benchmarkName, code));

this.self = module.invokeMember("get_associated_type");
Function<String,Value> getMethod = (name) -> module.invokeMember("get_method", self, name);

Value longList = getMethod.apply("generator").execute(self, LENGTH_OF_EXPERIMENT);

this.plusOne = getMethod.apply("plus_one");
this.sum = getMethod.apply("sum");

switch (benchmarkName) {
case "mapOverList": {
this.list = longList;
this.oldSum = sum.execute(self, longList, 0);
if (!this.oldSum.fitsInLong()) {
throw new AssertionError("Expecting a number " + this.oldSum);
}
break;
}
default:
throw new IllegalStateException("Unexpected benchmark: " + params.getBenchmark());
}
}

@Benchmark
public void mapOverList(Blackhole matter) {
performBenchmark(matter);
}

private void performBenchmark(Blackhole hole) throws AssertionError {
var newList = plusOne.execute(self, list);
var newSum = sum.execute(self, newList, 0);

var result = newSum.asLong() - oldSum.asLong();
if (result != LENGTH_OF_EXPERIMENT) {
throw new AssertionError("Unexpected result " + result);
}
hole.consume(result);
}
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.enso.interpreter.bench.benchmarks.semantic;
package org.enso.interpreter.bench.benchmarks.semantic;

import java.io.ByteArrayOutputStream;
import java.nio.file.Paths;
Expand Down
Loading

0 comments on commit 41b2aac

Please sign in to comment.