Skip to content

Conversation

@MinionJakub
Copy link
Contributor

This is full implementation of Ordered Maps, and it is also a continuation of Pull Request #136 and #288 .

MinionJakub and others added 30 commits June 19, 2024 08:44
First implementation of Ordered Map and Ordered Set
Small changes to address my mistakes in naming.
First implementation of queues based on implementation of Hood Melville queues from "Purely Functional Data Structures" Chris Okasaki.
Little correction of definitions.
Final touches before merge
@MinionJakub MinionJakub added the stdlib Work on standard library label Dec 18, 2025
Copy link
Collaborator

@wojpok wojpok left a comment

Choose a reason for hiding this comment

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

I have worked through this PR. Everything is much better then it used to be and hopefully soon everything will be ready. I have some minor issues. My comments apply to entire codebase, I simply didn't want to flood this discussion with repeated comments.

Interface is well polished. I suggest to do some name changes: member to mem and domain to keyList. Other than that, it is all fine.

Documentation requires overhaul. All sentences must start with capital letters and end with periods. Also docstrings need update. We don't support @brief label.


pub data Either X Y = Left of X | Right of Y

pub data Ord = Lt | Eq | Gt
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please, add show method for this datatype.

pub data Map Key = Map of {
T

{## @brief Creates empty map
Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't support @brief label. Simply write this statement and add a period at the end.

, empty : {Val} -> T Val

{## @brief Method to testing whether given map is empty or not
# @return True if it's empty false otherwise
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't add those # symbols in the middle of docstrings.


# Basic tests
let _ =
assert (IntMap.empty {Val=Bool} >.isEmpty);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Tests could be modernized to use the new testing framework.

, method foldr = mapFoldr
, method toList = toListT
, method toValueList = toValueListT
, method domain = domainT
Copy link
Collaborator

Choose a reason for hiding this comment

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

This naming is inconsistent. We either stick to domain/codomain or values/keys. I prefer latter honestly

, method insertChange = insertChangeT
, method remove = removeT
, method removeChange = removeChangeT
, method member = memberT
Copy link
Collaborator

Choose a reason for hiding this comment

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

Lists already uses mem, so I think it should be used here as well

each internal node has either two or three children, and all leaves are at the
same depth. Interestingly, the latter invariant is enforced by types, using
nested datatypes approach. To do so, we define the following two types.
# Due to frequent request for understanding this file please
Copy link
Collaborator

Choose a reason for hiding this comment

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

This bit needs to be rewritten. Don't announce any "popular demand", simply mention that the algorithm based on some resarch papers and enumerate them. It will be much more natural

| Black =>
match child with
| Node {value} =>
# Must be RED with Leaf children, by black-height invariant.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Some assertion to test this invariant would be nice

| Right colorLeftMin leftLeftMin valueLeftMin :: zipperr =>
deleteNearLeaf colorLeftMin leftLeftMin
(List.append zipperr (Left color valueLeftMin right :: zipper))
| _ => Leaf #Fail "postcondition"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it really fail or is just misplaced comment?

| Left colorRightMin valueRightMin rightRightMin :: zipperr =>
deleteNearLeaf colorRightMin rightRightMin
(List.append zipperr (Right color left valueRightMin :: zipper))
| _ => Leaf #Fail "postcondition"
Copy link
Collaborator

Choose a reason for hiding this comment

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

ditto

@ppolesiuk ppolesiuk requested a review from Copilot December 18, 2025 16:04
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements ordered maps using a red-black tree data structure, replacing the previous 2-3 tree implementation. The changes introduce a complete rewrite of the Map module with new API methods and update the comparison interface from separate equal and lt methods to a unified compare method returning an Ord type.

Key changes:

  • Complete reimplementation of Map using red-black trees instead of 2-3 trees
  • Introduction of Ord type (Lt, Eq, Gt) for comparisons
  • API updates including renamed methods (addinsert, memmember, foldfoldl) and new operations (union, update, mapVal, etc.)

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
lib/Map.fram Complete rewrite implementing red-black tree-based ordered maps with new API
lib/Base/Types.fram Added Ord data type for three-way comparisons
lib/Base/Int.fram Added compare method to Int type
lib/Base/String.fram Added compare method to String type
lib/Testing.fram Updated to use new Map API (addinsert, removed {key} syntax)
test/stdlib/stdlib0008_Map.fram Updated test suite with new API methods and added comprehensive test cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +3 to +5
# let compareI (x : Int) (y : Int) = if x < y then Lt else if x > y then Gt else Eq
# let compareS (x : String) (y : String) = if x < y then Lt else if x > y then Gt else Eq

Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Commented-out code should be removed. If these comparison functions are intended as examples or documentation, they should be uncommented or moved to proper documentation.

Suggested change
# let compareI (x : Int) (y : Int) = if x < y then Lt else if x > y then Gt else Eq
# let compareS (x : String) (y : String) = if x < y then Lt else if x > y then Gt else Eq

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +7
let (Map { module IntMap }) = Map.make {Key = Int}
let (Map { module StrMap }) = Map.make {Key = String}
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Inconsistent spacing in record syntax. Consider using consistent spacing: either {Key = Int} for both or {Key=Int} for both to match the style used elsewhere in the file.

Copilot uses AI. Check for mistakes.
# Organizes all registered tests into a tree structure
let Map {module SMap} = Map.make {Key=String}

let Map {module SMap} = Map.make {String}
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The Map.make call uses positional syntax {String} instead of the named parameter syntax {Key = String} used elsewhere. This is inconsistent with the API design shown in other files and may be confusing.

Suggested change
let Map {module SMap} = Map.make {String}
let Map {module SMap} = Map.make {Key = String}

Copilot uses AI. Check for mistakes.
match ar with
| AR_Ok repr => Tree repr
| AR_Split l k v r => Succ (Tree (Node2 l k v r))
# serachMin finds smallest element in a tree and builds zipper
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'serach' to 'search'.

Copilot uses AI. Check for mistakes.
match rr with
| RR_Ok node => Succ (Tree node)
| RR_Underflow repr => Tree repr
# serachMax finds largest element in a tree and builds zipper
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'serach' to 'search'.

Copilot uses AI. Check for mistakes.

# The father is red and no nephew is red.
# Then father becomes black and brother becomes red
# removing therfore the inequality of blackness.
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'therfore' to 'therefore'.

Copilot uses AI. Check for mistakes.

# The father is red and no nephew is red.
# Then father becomes black and brother becomes red
# removing therfore the inequality of blackness.
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

Corrected spelling of 'therfore' to 'therefore'.

Copilot uses AI. Check for mistakes.
| Right colorLeftMin leftLeftMin valueLeftMin :: zipperr =>
deleteNearLeaf colorLeftMin leftLeftMin
(List.append zipperr (Left color valueLeftMin right :: zipper))
| _ => Leaf #Fail "postcondition"
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The error handling here is unclear. The comment suggests a postcondition failure, but returning Leaf silently hides this error condition. Consider using a proper error mechanism or at minimum improving the error message to explain what postcondition failed.

Copilot uses AI. Check for mistakes.
| Left colorRightMin valueRightMin rightRightMin :: zipperr =>
deleteNearLeaf colorRightMin rightRightMin
(List.append zipperr (Right color left valueRightMin :: zipper))
| _ => Leaf #Fail "postcondition"
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

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

The error handling here is unclear. The comment suggests a postcondition failure, but returning Leaf silently hides this error condition. Consider using a proper error mechanism or at minimum improving the error message to explain what postcondition failed.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stdlib Work on standard library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants