Skip to content

Vector module (#258)#306

Open
kulson wants to merge 11 commits intofram-lang:masterfrom
kulson:258
Open

Vector module (#258)#306
kulson wants to merge 11 commits intofram-lang:masterfrom
kulson:258

Conversation

@kulson
Copy link
Contributor

@kulson kulson commented Jan 10, 2026

Vector module implementation with tests

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.

Overall this PR is a nice addition to standard library. Some cosmetic changes are required. As far as I am concerned the logic is correct, so there shouldn't be much work to do


let vec = makeVector {T=Int} 4
let _ =
assert (vec.size == 4);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please, use designated testing framework for testing. Use String tests as an example

lib/Vector.fram Outdated
parameter T

# Helper methods for vector implementation.
method getContentAt (Vector{ content } : Vector E T) (n : Int) =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Formatting is off here. We usually write Vector {content} or Vector { content }. This extra space before opening bracket is somewhat mandatory. This applies to entire file

lib/Vector.fram Outdated
let n = n ||| (n>>64) in
n+1

pub let makeVector {T} (size : Int) =
Copy link
Collaborator

Choose a reason for hiding this comment

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

Documentation is missing. Please, add docstrings to all public functions and methods

lib/Vector.fram Outdated
#}
pub method resize (self : Vector E T) (n : Int) =
self.setSize n;
let new_capacity = getSmallestUpperPow2 n in
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use camelCase for variables

lib/Vector.fram Outdated

# Get the nth element of a vector.
pub method get (self : Vector E T) (n : Int) =
assert {msg="Vector is empty"} (self.empty.neg);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This assert is excessive. Second one is sufficient on its own

lib/Vector.fram Outdated
self.setSize 0;
self.setCapacity 0;
self.setContent (self.mut.makeArray 0 (None : Option T))

No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

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

This interface is pretty minimal. It would be nice to have al least iter and fold methods, as well as toList/fromList conversion functions.

@kulson
Copy link
Contributor Author

kulson commented Jan 12, 2026

This commit includes almost all the fixes you mentioned. At this point, I haven't implemented iter and fold methods yet.

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.

Code is much cleaner now. Upon more careful study I have found more issues with the code

lib/Vector.fram Outdated
parameter E
parameter T

{## Helper methods for vector implementation ##}
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is comment about internal implementation, it shouldn't be a part of a documentation. Single # would be enough. Also, please add ## ## Vector library at the top of the file. Title is missing

lib/Vector.fram Outdated
import open /Base/Bool
import open /List

data Vector E T = Vector of
Copy link
Collaborator

Choose a reason for hiding this comment

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

Make this type abstract. It is visible outside the module, so users should be able to use this type to annotate stuff

lib/Vector.fram Outdated
n+1

{## Creates vector of given size (filled with Nones). ##}
pub let makeVector {T} (size : Int) =
Copy link
Collaborator

Choose a reason for hiding this comment

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

You have hardcoded ioMut handler, which disallows using vectors with other mutability handlers. Desired handler should be passed as an argument to this function.

lib/Vector.fram Outdated
pub method empty (Vector {size} : Vector E T) = size.get == 0

{##
Resizes the vector to contain n elements.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Content of multiline docstrings should be indented with 2 spaces

import open /Vector
import open Testing

let vec = makeVector {T=Int} 4
Copy link
Collaborator

Choose a reason for hiding this comment

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

Tests needs rework. All actions should be performed inside testCase body. Since IO is hardcoded into vector, it is impossible to fix right now. Once you enable other handlers, rework those tests. You will need to make a second wrapper, that will provide mut handler. Tests will look like this:

testCase "test" (fn _ =>
  hMut (fn mut =>
    # test body
    ))

lib/Vector.fram Outdated
size := n

{## Returns maximum size of vector. ##}
pub let maxVectorSize = maxArrayLength
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is wrong. Vectors are always aligned to the nearest power of 2. Arrays can have any arbitrary size, so there might a case where maxVectorSize is not possible to allocate. For example if maxArrayLength is 17, then the vector of size 17 will require internal array of size 32, which is impossible to allocate. I suggest deleting this. I don't think there is any demand for contant such as this one

assertEqS cap 4);

testCase "checks max vector size constant" (fn _ =>
assertEqS maxVectorSize maxArrayLength);
Copy link
Collaborator

Choose a reason for hiding this comment

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

This shouldn't be tested at all

@kulson
Copy link
Contributor Author

kulson commented Jan 17, 2026

Tests are not working yet because the testing framework is still pending merge.

@Xaro8 Xaro8 self-requested a review January 29, 2026 11:24
Copy link

@Xaro8 Xaro8 left a comment

Choose a reason for hiding this comment

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

Overall looks fine to me. Agree that fold left/right may be useful, also maybe resize functionality should be tested (outside of push_back).

@kulson
Copy link
Contributor Author

kulson commented Feb 1, 2026

From now on the vector no longer stores Option T.
Instead it uses a dbl_magic, which means the value must be initialized before reading.

@wojpok
Copy link
Collaborator

wojpok commented Feb 3, 2026

I think, you were meant to use dbl_magic to convert number 0 to desired type T. The difference is subtle, but if we ever were to compile this code, zero (null) will not require any additional memory allocation. Also, please fix issues with last lines in files. Last line should be completely empty

Copy link
Member

@ppolesiuk ppolesiuk left a comment

Choose a reason for hiding this comment

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

Overall, everything is going in the right direction. The interface is minimal, but it can be later extended. One thing must be fixed before merging: the public interface must not allow to read values created with dbl_magic. Other thing that might be rethought is when, and how the capacity should be changed. See comments below, for details.

log' LT_Exit (msg.unwrapOr "Test terminated early.");
testExit ()
end
end No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

New line character was removed from non changed file. First, we should avoid unnecessary changes like this, second, we use UNIX convention, where each nonempty text file ends with newline character.

lib/Vector.fram Outdated
let n = n ||| (n>>8) in
let n = n ||| (n>>16) in
let n = n ||| (n>>32) in
let n = n ||| (n>>64) in
Copy link
Member

Choose a reason for hiding this comment

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

For 64-bit (or even 63) integers this line changes nothing. It can be safely removed.

pub let makeVector {T, ~mut : Mut E} (size : Int) =
let capacity = (getSmallestUpperPow2 size) in
Vector
{ content = ~mut.ref (~mut.makeArray capacity ((extern dbl_magic : Int -> T) 0))
Copy link
Member

Choose a reason for hiding this comment

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

The extern dbl_magic is used to create an element of arbitrary type. This would be correct unless the public interface allows reading such elements. For example,

let v = makeVector {T=String, ~mut=ioMut} 42 in v.get 0 + "A"

results in runtime error. To avoid such problems we should forbid creating non-empty vectors without providing the default value. Creating empty vectors is still ok.

lib/Vector.fram Outdated

{## Converts a list into a vector. ##}
pub let fromList {~mut : Mut E} (l : List T) =
let vec = makeVector {T=T} l.length in
Copy link
Member

Choose a reason for hiding this comment

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

This line could be slightly simplified.

Suggested change
let vec = makeVector {T=T} l.length in
let vec = makeVector {T} l.length in

This is not very important comment.

lib/Vector.fram Outdated
self.setSize 0;
self.setCapacity 0;
self.setContent (self.mut.makeArray 0 ((extern dbl_magic : Int -> T) 0))

No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

This file ends with a space (without a new line).

let lst = vec.back in
assertEqS fst "abc";
assertEqS lst "a"))

No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

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

File ends with a space (as above)

Copy link
Member

Choose a reason for hiding this comment

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

I don't see, why the capacity of a vector must be a power of 2 (except for the empty vector). Maybe it would be better to allow any capacity, and multiply it by 2 (or any other constant), when the vector must be resized. This would allow to convert lists to vectors without unnecessary memory overhead, and I thing would slightly simplify the code. Moreover, I don't see why the vector is never resized down.

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.

4 participants