Skip to content

[ fix ] Seq1#68

Merged
Matthew-Mosior merged 4 commits into
idris-community:mainfrom
Matthew-Mosior:fix-seq1-to-make-thread-safe
Mar 27, 2026
Merged

[ fix ] Seq1#68
Matthew-Mosior merged 4 commits into
idris-community:mainfrom
Matthew-Mosior:fix-seq1-to-make-thread-safe

Conversation

@Matthew-Mosior

Copy link
Copy Markdown
Collaborator

This PR fixes the Seq1 implementation by utilizing casupdate1 to ensure thread-safe operations.

Closes #63

@Matthew-Mosior
Matthew-Mosior marked this pull request as ready for review March 24, 2026 18:25
@Matthew-Mosior

Copy link
Copy Markdown
Collaborator Author

@stefan-hoeck Would appreciate a review on the fixes to Seq1.

Comment thread src/Data/Seq1/Unsized.idr Outdated
export
data Seq1 : (s : Type) -> (e : Type) -> Type where
MkSeq1 : Ref s (FingerTree (Elem e))
MkSeq1 : Ref s (Seq e)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just a minor thing, but would using FingerTree directly here not make the rest of the code simpler. In the utility function that follow, there is usually an extra unwrapping step followed by a rewrapping, which - in my opinion - makes the code harder to read.

Comment thread src/Data/Seq1/Unsized.idr Outdated
let tr # t := ref1 Empty t
in (MkSeq1 tr) # t
let seq # t := ref1 (MkSeq Empty) t
in (MkSeq1 seq) # t

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd suggest to not use parens here. They are not needed and hamper readability. Same in many other functions.

Comment thread src/Data/Seq1/Unsized.idr Outdated
let tr' # t := read1 tr t
in casswap1 tr (reverseTree id tr') t
reverse (MkSeq1 seq) t =
casupdate1 seq (\(MkSeq tr) => (MkSeq (reverseTree id tr), ())) t

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use casmod1 instead of casupdate1 here.

Comment thread src/Data/Seq1/Unsized.idr Outdated
let tr' # t := read1 tr t
in casswap1 tr (MkElem a `consTree` tr') t
(a `cons` (MkSeq1 seq)) t =
casupdate1 seq (\(MkSeq tr) => (MkSeq (MkElem a `consTree` tr), ())) t

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Whenever the return type of a a CAS-loop is Unit, use casmod1 instead of casupdate1.

Comment thread src/Data/Seq1/Unsized.idr Outdated
Comment on lines +98 to +103
in case viewLTree tr of
Just (MkElem a, _) =>
Just a # t
Nothing =>
Nothing # t

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'd suggest to use map here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'm confused as to what map will do here for implementing head?

Comment thread src/Data/Seq1/Unsized.idr Outdated
Comment on lines +109 to +117
tail (MkSeq1 seq) t =
casupdate1 seq
(\(MkSeq tr) =>
case viewLTree tr of
Just (MkElem _, tr') =>
(MkSeq tr', MkSeq1 seq)
Nothing =>
(MkSeq tr, MkSeq1 seq)
) t

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If the goal of this function is in-place mutation, there is no need for a return value and it should probably just be F1' s. If the goal is to return the tail of the internal sequence, it should by typed and implemented similar to head. Currently, head and tail are somewhat at odds.

Comment thread src/Data/Seq1/Unsized.idr Outdated
Comment on lines +123 to +131
viewr (MkSeq1 seq) t =
casupdate1 seq
(\(MkSeq tr) =>
case viewRTree tr of
Just (tr', MkElem a) =>
(MkSeq tr', Just (MkSeq1 seq, a))
Nothing =>
(MkSeq tr, Nothing)
) t

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Again, it is not clear why this should return a pair of values.

@Matthew-Mosior Matthew-Mosior Mar 26, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

viewl and viewr want to return a pair of values as these functions serve as "views", they allow inspection of either the first or last element, and pairs either the Seq1 without the first element (viewl), or Seq1 without the last element (viewr).

Comment thread src/Data/Seq1/Unsized.idr Outdated
seq'' # t
Nothing =>
empty t
init (MkSeq1 seq) t =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

init and last are again at odds, similar to head and tail.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I think the idea is that head and last are inverses, as are init and tail.

head and last are non-mutating, and init and tail are mutating.

@stefan-hoeck

Copy link
Copy Markdown
Member

I added some opinionated comments. Thread-safety looks OK, but should ideally be tested for all functions not only one. The semantics of some of the functions is not always clear: See the comment for head, which reads and returns a value, and tail, which mutates the reference. Likewise for init and last.

Mere mutation functions should probably always be of type F1' s and not return a new wrapper for the mutable ref. That just makes downstream code more cumbersome.

@Matthew-Mosior

Copy link
Copy Markdown
Collaborator Author

I added some opinionated comments. Thread-safety looks OK, but should ideally be tested for all functions not only one. The semantics of some of the functions is not always clear: See the comment for head, which reads and returns a value, and tail, which mutates the reference. Likewise for init and last.

Mere mutation functions should probably always be of type F1' s and not return a new wrapper for the mutable ref. That just makes downstream code more cumbersome.

Thank you for your thorough review. I've addressed all comments, and have made quite a few changes based on them.

I think this is in a good spot for a re-review.

@Matthew-Mosior
Matthew-Mosior merged commit 11a553f into idris-community:main Mar 27, 2026
2 checks passed
@Matthew-Mosior
Matthew-Mosior deleted the fix-seq1-to-make-thread-safe branch March 27, 2026 13:11
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.

[ concurrency ] mutable containers are not thread safe, even though they claim to be

2 participants