Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutable references and arrays #126

Merged
merged 6 commits into from
Dec 20, 2024
Merged

Mutable references and arrays #126

merged 6 commits into from
Dec 20, 2024

Conversation

ppolesiuk
Copy link
Member

The library is not complete yet:

  • Some useful methods are missing, like map or foldLeft
  • There are not tests of the implementation
  • The documentation is missing,

But I would like to someone review the general approach, and in particular, to make sure that it fits well to our effect system and philosophy.

@ppolesiuk ppolesiuk requested review from forell and Foxinio June 15, 2024 14:23
@ppolesiuk ppolesiuk marked this pull request as draft June 15, 2024 14:23
Copy link
Collaborator

@Foxinio Foxinio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While some minor changes can be made, and a lot of functionality is missing, i think it's a great addition and i didn't manage to break it so that's good.

lib/Mutable.fram Show resolved Hide resolved
lib/Mutable.fram Outdated
extern dbl_ref : T ->[E] Ref E T

pub method pureInitArray {E, self : Mut E} (n : Int) (f : _ ->[E] _) =
if n <= 0 then unsafeMakeArray {E} 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should "raise" some sort of an error if user tries to create array with negative length, because that's a mistake and i think user should know that he's making it.
Maybe use standard `re and let user handle it himself

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, and the decision was hard. Should we allow function f to also call pureInitArray, in order to create matrices? If yes, it should be able to raise some effects in f. However, this implementation will behave incorrectly if we allow function f to do some non-linear effects, like backtracking, and therefore I restricted its effect to just [E].

In order to allow f to raise some effects, we have to make sure that these effects are linear. For a moment, I thought that we could have some standard RE effect signature in Prelude like the following.

data RE (effect E) =
  { raise : {type T, ?msg : String } -> Unit ->[E] T }

However, its not true that all instances of this effect are linear. Having re : RE StdRE, we can write:

let badRE = RE {effect=[StdRE, BT], raise = re.raise }

to have the instance of RE with nonlinear effect (it contains backtracking).

let unsafeSetArray {E, type T} =
extern dbl_arraySet : Array E T -> Int -> T ->[E] Unit

let unsafeMakeArray {E, type T} =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we are using ocaml for evaluation, we might want to restrict length of arrays, to one we are allowed to create in ocaml (Sys.max_array_length).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. But what should we do when the user tries to create too large array?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a discussion @ppolesiuk suggested that we could take an approach similar to OCaml and expose the maximum array length for when the programmer requires this kind of assurance. I think this is reasonable, given that oversized allocations are uncommon and recovery from out of memory situations is rarely needed/desirable (at least for such a high level language). However, perhaps we still want to check the size somewhere, to display a custom error instead of OCaml's.

lib/Mutable.fram Outdated Show resolved Hide resolved
src/Eval.ml Outdated Show resolved Hide resolved
Copy link
Member

@forell forell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing functionality aside, I think the interface exposed by this library looks like it'll be nice to use and fits the rest of Fram well enough.

These changes seem to motivate some form of linear effects down the line, but since that might require considerable design and implementation effort, I think these additions should be merged before that's addressed.

let unsafeSetArray {E, type T} =
extern dbl_arraySet : Array E T -> Int -> T ->[E] Unit

let unsafeMakeArray {E, type T} =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a discussion @ppolesiuk suggested that we could take an approach similar to OCaml and expose the maximum array length for when the programmer requires this kind of assurance. I think this is reasonable, given that oversized allocations are uncommon and recovery from out of memory situations is rarely needed/desirable (at least for such a high level language). However, perhaps we still want to check the size somewhere, to display a custom error instead of OCaml's.

lib/Mutable.fram Outdated
pub method set {E, type T, self : Ref E T} =
(extern dbl_refSet : Ref E T -> T ->[E] Unit) self

pub method set {E, type T, self : Array E T} (n : Int) v =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming this method set has a funny consequence for the (:=) operator.

> (a := 3) 42;;
: Unit
= <ctor>
> a.get 3;;
: Int
= 42

This is pretty unintuitive. We could rename the method that (:=) desugars to, rename this method on arrays, or remove it altogether (then we still have at for modification).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe rename this method to setAt?

@ppolesiuk
Copy link
Member Author

The implementation of arrays is ready. There is still missing functionality, and maximum size of an array is not implemented, but I think these things can be done as a separate issue.

@ppolesiuk ppolesiuk requested review from Foxinio and forell December 18, 2024 11:53
@ppolesiuk ppolesiuk marked this pull request as ready for review December 18, 2024 11:54
Copy link
Member

@forell forell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this PR is ready for merging now. As discussed, the handlers that are currently expressed as higher-order functions should be changed to first-class handlers once they can be annotated properly.

Copy link
Collaborator

@Foxinio Foxinio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only change I would maybe want to see (being array length constant) can be done in separate PR.

@ppolesiuk ppolesiuk merged commit f5eb1fa into master Dec 20, 2024
2 checks passed
@ppolesiuk ppolesiuk deleted the arrays branch December 20, 2024 19:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants