-
Notifications
You must be signed in to change notification settings - Fork 16
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
Conversation
There was a problem hiding this 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
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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} = |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this 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} = |
There was a problem hiding this comment.
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 = |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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
?
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. |
There was a problem hiding this 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.
There was a problem hiding this 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.
The library is not complete yet:
map
orfoldLeft
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.