Skip to content

Refactor codebase to be immutable#6

Merged
francescovallone merged 6 commits into
avesbox:mainfrom
dickermoshe:main
Apr 9, 2025
Merged

Refactor codebase to be immutable#6
francescovallone merged 6 commits into
avesbox:mainfrom
dickermoshe:main

Conversation

@dickermoshe

@dickermoshe dickermoshe commented Apr 9, 2025

Copy link
Copy Markdown
Contributor

Description

Acanthis will eventually add a Code Generator for creating datamodels which contain validation.
This is the 1st step in making everything more @annotation friendly by making everything const.

To aid the process of working with immutable containers, I've added fast_immutable_collections which is a well maintained package for doing just that.

We will need to make the versions constraints of meta and fast_immutable_collections more flexible before merging.

This may be breaking if you consider addCheck a public API . I renamed it to withCheck because that's a better description.

If this get's merged I will start working on making the built-in checks and transforms into classes.

Fixes: N/A

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Summary by CodeRabbit

  • New Features

    • Introduced flexible instantiation patterns with support for asynchronous and synchronous validations across various type validators.
  • Refactor

    • Updated validation methods to return new immutable instances, allowing clearer method chaining and improved reliability.
  • Chores

    • Upgraded external dependencies to bolster immutability support.
  • Tests

    • Added new test cases verifying constant instantiation and consistent behavior across all validation types.

@coderabbitai

coderabbitai Bot commented Apr 9, 2025

Copy link
Copy Markdown

Walkthrough

The changes update several Acanthis type classes by introducing constant constructors with optional parameters for operations and asynchronous flags. Methods that previously modified instances now return new immutable instances using withCheck, withAsyncCheck, and withTransformation. The base type and its related classes are refactored to use immutable collections and annotations, and the AcanthisMap class has been overhauled for stricter immutability. Corresponding test suites have been enhanced with new cases verifying the const instantiation and functional behavior across the library.

Changes

File(s) Change Summary
lib/.../{boolean.dart, date.dart, list.dart, nullable.dart, number.dart, string.dart, union.dart} Converted constructors to const with optional parameters; refactored validation methods to use withCheck, withAsyncCheck, and withTransformation that return new immutable instances.
lib/.../map.dart Enforced immutability by replacing mutable maps with IMap and UnmodifiableMapView; added a constant constructor and updated methods to return new instances for operations (e.g., extend, merge, pick).
lib/.../types.dart Updated the base type to use immutable collections (IList), removed mutable fields, and added @immutable annotations to core types and operations; refactored check and transformation methods.
pubspec.yaml Added dependencies: fast_immutable_collections: ^11.0.4 and meta: ^1.16.0.
test/.../*.dart Added test cases across type tests (boolean, date, list, nullable, number, string, union, pipe) to verify const instantiation and pipeline transformation functionality.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant I as Instance (AcanthisType)
    participant N as New Instance

    U->>I: Create instance (const constructor)
    U->>I: Call withCheck(check)
    I->>N: Return new instance with check added
    U->>N: Call withAsyncCheck(asyncCheck)
    N->>I: Return new instance with async check applied
    U->>I: Call withTransformation(transformation)
    I->>N: Return new instance with transformation applied
Loading

Poem

In the garden of code I happily roam,
With const constructors, I find a new home.
Immutable hops and async checks in my trail,
Transformations bloom—oh, what a tale!
I’m a code rabbit, joyful and bright,
Bouncing lively through each line of delight.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60b30ac and 352f13c.

📒 Files selected for processing (1)
  • lib/src/types/types.dart (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/src/types/types.dart

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
lib/src/types/types.dart (1)

11-13: Use a typed IList for stronger type safety
While storing all operations in an IList helps maintain immutability, consider using a generic type parameter (e.g., IList<AcanthisOperation<O>>) for better compile-time checks and explicitness.

- final IList operations;
+ final IList<AcanthisOperation<O>> operations;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f15aa86 and 60b30ac.

⛔ Files ignored due to path filters (1)
  • pubspec.lock is excluded by !**/*.lock
📒 Files selected for processing (18)
  • lib/src/types/boolean.dart (2 hunks)
  • lib/src/types/date.dart (2 hunks)
  • lib/src/types/list.dart (2 hunks)
  • lib/src/types/map.dart (8 hunks)
  • lib/src/types/nullable.dart (2 hunks)
  • lib/src/types/number.dart (2 hunks)
  • lib/src/types/string.dart (6 hunks)
  • lib/src/types/types.dart (9 hunks)
  • lib/src/types/union.dart (2 hunks)
  • pubspec.yaml (1 hunks)
  • test/types/boolean_test.dart (1 hunks)
  • test/types/date_test.dart (1 hunks)
  • test/types/list_test.dart (1 hunks)
  • test/types/nullable_test.dart (1 hunks)
  • test/types/number_test.dart (1 hunks)
  • test/types/pipe_test.dart (1 hunks)
  • test/types/string_test.dart (1 hunks)
  • test/types/union_test.dart (1 hunks)
🔇 Additional comments (102)
test/types/union_test.dart (1)

6-8: Good addition of const constructor test!

This test case verifies that AcanthisUnion can be instantiated with the const keyword, which is essential for the immutability refactoring mentioned in the PR objectives. This aligns perfectly with the goal of transitioning towards an immutable codebase.

test/types/date_test.dart (1)

8-10: Great test for immutability support!

This test case confirms that AcanthisDate can be instantiated with the const keyword, which is consistent with the immutability changes being implemented across the codebase. This helps ensure that the class supports compile-time constants.

test/types/nullable_test.dart (1)

6-8: Good test for nested immutable types!

This test demonstrates that AcanthisNullable can be instantiated as a constant with another immutable type (AcanthisDate) as a parameter. This is an excellent addition that ensures the immutability refactoring works with nested type compositions.

pubspec.yaml (1)

24-25: Excellent choice of dependencies for immutability!

The addition of fast_immutable_collections aligns perfectly with the PR's objective of refactoring the codebase to be immutable. This library provides efficient immutable data structures that will help maintain immutability throughout the codebase.

The meta package addition is also appropriate, as it provides annotations that can help enforce immutability constraints and improve code documentation.

test/types/boolean_test.dart (1)

6-8: LGTM: Good addition of test for const instantiation.

This test verifies that AcanthisBoolean can be created using a const constructor, which is aligned with the PR objective of making the codebase immutable.

test/types/string_test.dart (1)

7-9: LGTM: Proper verification of const constructor support.

This test correctly verifies that AcanthisString supports constant instantiation, which is consistent with the immutability goals of the PR.

test/types/list_test.dart (1)

7-9: LGTM: Good test for const constructor with argument.

This test confirms that AcanthisList can be instantiated as a constant with an AcanthisDate parameter, aligning with the immutability objectives.

test/types/pipe_test.dart (2)

4-6: LGTM: Simple identity function for testing purposes.

This helper function serves as a pass-through for DateTime objects, appropriately used in the const constructor test.


10-16: LGTM: Comprehensive const constructor test for AcanthisPipeline.

This test properly verifies that AcanthisPipeline supports constant instantiation with all required parameters (inType, outType, and transform function). The test is well-structured and uses the previously defined helper function.

test/types/number_test.dart (1)

8-10: Good addition - const constructor test enhances coverage.

This test case verifies that AcanthisNumber can be instantiated with the const keyword, supporting the PR's immutability initiative. This aligns perfectly with the PR objective to refactor the codebase for immutability.

lib/src/types/boolean.dart (6)

6-9: Excellent conversion to const constructor.

The addition of a constant constructor with optional parameters for operations and isAsync supports the immutability refactoring goal while maintaining backward compatibility.


13-16: Good implementation of immutability pattern.

Replacing the previous implementation with withCheck that returns a new instance rather than modifying the current one follows proper immutability practices.


21-24: Good implementation of immutability pattern.

Similar to isTrue(), this method now correctly returns a new instance with the check applied rather than modifying the existing instance.


37-43: Well-structured implementation of withAsyncCheck.

This method correctly creates a new immutable instance with the added async check while setting the isAsync flag to true. The implementation properly leverages immutable collections.


45-50: Clean implementation of withCheck.

The method properly creates a new immutable instance with the added check, following the immutability pattern consistently.


52-58: Consistent implementation of withTransformation.

This implementation follows the same pattern as the other methods, creating a new immutable instance with the transformation added to operations.

lib/src/types/union.dart (4)

10-10: Clean conversion to const constructor with optional parameters.

The constructor now accepts optional parameters for operations and isAsync while maintaining the original elements parameter, providing a good balance of backward compatibility and new functionality.


54-61: Well-implemented withAsyncCheck method.

This method correctly creates a new AcanthisUnion instance while preserving the existing elements and adding the new check to operations. Setting isAsync to true is appropriate for async operations.


63-69: Clean implementation of withCheck method.

The method follows the same pattern as withAsyncCheck but without setting isAsync, which is appropriate for synchronous checks.


71-77: Consistent implementation of withTransformation.

This implementation matches the pattern of the other methods, maintaining consistent behavior throughout the class.

lib/src/types/date.dart (7)

7-10: Good conversion to const constructor.

The constructor now properly supports immutability with optional parameters for operations and isAsync, consistent with other type classes in the library.


14-18: Good implementation of min method.

The method now returns a new instance with the check applied rather than modifying the existing instance, properly following immutability principles.


23-27: Good implementation of differsFromNow method.

Similar to the min method, this implementation now correctly returns a new instance with the check applied.


32-36: Good implementation of max method.

This method also correctly returns a new instance with the check applied, maintaining consistency with the other methods.


49-55: Well-structured implementation of withAsyncCheck.

This method correctly creates a new AcanthisDate instance with the async check added to operations and isAsync set to true.


57-62: Clean implementation of withCheck.

The method properly creates a new immutable instance with the added check, following the immutability pattern consistently.


64-70: Consistent implementation of withTransformation.

This implementation maintains the same pattern as the other methods, creating a new immutable instance with the transformation added to operations.

lib/src/types/nullable.dart (4)

11-12: Constructor updated to support immutability pattern.

The constructor now uses the const keyword and accepts super.operations and super.isAsync parameters, enabling the creation of immutable instances that can still be validated and transformed.


63-69: New method adds async check support while preserving immutability.

The withAsyncCheck method properly creates a new instance with the additional check added to the operations collection, maintaining immutability by not modifying the original instance. It also correctly sets isAsync to true.


71-75: New method adds synchronous check support while preserving immutability.

The withCheck method correctly creates a new instance with the additional check, maintaining immutability by not modifying the original instance.


77-82: New method adds transformation support while preserving immutability.

The withTransformation method correctly creates a new instance with the additional transformation, maintaining immutability by not modifying the original instance.

lib/src/types/list.dart (11)

7-7: Element property made final to ensure immutability.

Making the element property final ensures it cannot be modified after initialization, which supports the immutable codebase objective.


9-9: Constructor updated to support immutability pattern.

The constructor now uses the const keyword and accepts named parameters super.operations and super.isAsync, enabling the creation of immutable instances.


96-99: Method refactored to use immutable pattern.

The min method now correctly returns a new instance with the check added, rather than modifying the current instance. This promotes immutability.


104-107: Method refactored to use immutable pattern.

The anyOf method now correctly returns a new instance with the check added, rather than modifying the current instance.


112-116: Method refactored to use immutable pattern.

The everyOf method now correctly returns a new instance with the check added, rather than modifying the current instance.


121-124: Method refactored to use immutable pattern.

The max method now correctly returns a new instance with the check added, rather than modifying the current instance.


131-134: Method refactored to use immutable pattern.

The unique method now correctly returns a new instance with the check added, rather than modifying the current instance.


139-143: Method refactored to use immutable pattern.

The length method now correctly returns a new instance with the check added, rather than modifying the current instance.


145-152: New method adds async check support while preserving immutability.

The withAsyncCheck method properly creates a new instance with the additional check added to the operations collection. It correctly sets isAsync to true, which is important for async validation.


154-160: New method adds synchronous check support while preserving immutability.

The withCheck method correctly creates a new instance with the additional check, maintaining immutability by not modifying the original instance.


162-169: New method adds transformation support while preserving immutability.

The withTransformation method correctly creates a new instance with the additional transformation, maintaining immutability by not modifying the original instance.

lib/src/types/number.dart (19)

9-9: Constructor updated to support immutability pattern.

The constructor now uses the const keyword and accepts optional parameters super.isAsync and super.operations, enabling the creation of immutable instances with validation capabilities.


13-16: Method refactored to use immutable pattern.

The lte method now correctly returns a new instance with the check added, rather than modifying the current instance. This promotes immutability.


21-24: Method refactored to use immutable pattern.

The gte method now correctly returns a new instance with the check added, rather than modifying the current instance.


28-31: Method refactored to use immutable pattern.

The between method now correctly returns a new instance with the check added, rather than modifying the current instance.


36-39: Method refactored to use immutable pattern.

The gt method now correctly returns a new instance with the check added, rather than modifying the current instance.


44-47: Method refactored to use immutable pattern.

The lt method now correctly returns a new instance with the check added, rather than modifying the current instance.


52-55: Method refactored to use immutable pattern.

The positive method now correctly returns a new instance with the check added, rather than modifying the current instance.


60-63: Method refactored to use immutable pattern.

The negative method now correctly returns a new instance with the check added, rather than modifying the current instance.


68-71: Method refactored to use immutable pattern.

The integer method now correctly returns a new instance with the check added, rather than modifying the current instance.


76-79: Method refactored to use immutable pattern.

The double method now correctly returns a new instance with the check added, rather than modifying the current instance.


84-87: Method refactored to use immutable pattern.

The multipleOf method now correctly returns a new instance with the check added, rather than modifying the current instance.


92-95: Method refactored to use immutable pattern.

The finite method now correctly returns a new instance with the check added, rather than modifying the current instance.


100-103: Method refactored to use immutable pattern.

The infinite method now correctly returns a new instance with the check added, rather than modifying the current instance.


108-111: Method refactored to use immutable pattern.

The nan method now correctly returns a new instance with the check added, rather than modifying the current instance.


116-119: Method refactored to use immutable pattern.

The notNaN method now correctly returns a new instance with the check added, rather than modifying the current instance.


129-130: Method refactored to use immutable transformation pattern.

The pow method now correctly returns a new instance with the transformation added, rather than modifying the current instance.


138-141: New method adds async check support while preserving immutability.

The withAsyncCheck method properly creates a new instance with the additional check added to the operations collection. It correctly sets isAsync to true, which is important for async validation.


143-146: New method adds synchronous check support while preserving immutability.

The withCheck method correctly creates a new instance with the additional check, maintaining immutability by not modifying the original instance.


148-152: New method adds transformation support while preserving immutability.

The withTransformation method correctly creates a new instance with the additional transformation, maintaining immutability by not modifying the original instance.

lib/src/types/string.dart (8)

39-39: Constructor updated to support immutability pattern.

The constructor now uses the const keyword and accepts optional parameters super.isAsync and super.operations, enabling the creation of immutable instances with validation capabilities.


43-46: Method refactored to use immutable pattern.

The email method now correctly returns a new instance with the check added, rather than modifying the current instance. This promotes immutability.


207-224: Method refactored to use immutable async check pattern.

The uncompromised method now correctly uses withAsyncCheck to create a new instance with the async check added, rather than modifying the current instance. The implementation maintains the same validation logic while adopting the immutable pattern.


359-360: Method refactored to use immutable transformation pattern.

The encode method now correctly returns a new instance with the transformation added, rather than modifying the current instance.


365-367: Method refactored to use immutable transformation pattern.

The decode method now correctly returns a new instance with the transformation added, rather than modifying the current instance.


392-395: New method adds async check support while preserving immutability.

The withAsyncCheck method properly creates a new instance with the additional check added to the operations collection. It correctly sets isAsync to true, which is important for async validation.


397-400: New method adds synchronous check support while preserving immutability.

The withCheck method correctly creates a new instance with the additional check, maintaining immutability by not modifying the original instance.


402-406: New method adds transformation support while preserving immutability.

The withTransformation method correctly creates a new instance with the additional transformation, maintaining immutability by not modifying the original instance.

lib/src/types/types.dart (11)

1-2: Imports for immutability approach
The addition of fast_immutable_collections and meta packages is appropriate for fostering immutability and leveraging @immutable annotations.


8-8: Immutability annotation
Applying @immutable to AcanthisType aligns with the immutability goal, clarifying that instances shouldn't be mutated after creation.


16-17: Constructor defaults
Specifying operations as IList.empty() and isAsync as false by default is consistent with a purely immutable design. This ensures any derived classes or extended types have a safe default.


117-118: Abstract method for adding checks
The new withCheck method signals an immutable pattern: returning a new AcanthisType instead of mutating. This consistently aligns with the rest of the refactoring.


121-122: Async check method
withAsyncCheck neatly parallels withCheck, ensuring asynchronous operations are handled via a separate pipeline.


133-134: Refine with synchronous checks
Delegating the refine method to withCheck elegantly ensures consistency across custom checks.


142-142: Refine with asynchronous checks
Similar to refine, refineAsync uses withAsyncCheck to preserve the functional immutable flow.


155-155: Introducing withTransformation
Allowing transformations to be appended without mutating the existing AcanthisType object is key to maintaining a purely functional style.


159-159: transform uses withTransformation
This change ensures the transform method produces a new, updated instance, further cementing immutability.


165-165: @immutable for all operation classes
Marking AcanthisCheck, AcanthisAsyncCheck, AcanthisTransformation, AcanthisOperation, AcanthisPipeline, and AcanthisParseResult as @immutable ensures every part of the type-checking/transforming workflow remains immutable.

Also applies to: 191-191, 217-217, 233-233, 242-242, 321-321


250-250: const AcanthisPipeline constructor
Declaring the pipeline constructor as const encourages compile-time instantiation and ensures pipeline objects remain fully immutable.

lib/src/types/map.dart (22)

5-6: Imports for immutability
Importing fast_immutable_collections and meta supports the new immutable container usage and annotation strategy.


14-15: Immutable fields and public getter
Changing _fields to an IMap and exposing it via an unmodifiable view ensures external code can’t mutate the map contents. Well done.


17-19: Private flags and lists
Keeping _passthrough, _dependencies, and _optionalFields as private final fields enforces immutability of these properties, reducing side effects.


21-26: Primary constructor with defaults
Declaring the main constructor as const and assigning empty IList defaults aligns with the move to a fully immutable data structure.


27-37: Private named constructor
Introducing AcanthisMap._ to handle complex initialization while preserving const usage is a clean approach for building new instances with updated fields.


267-274: Marking fields optional
Using _optionalFields.addAll(fields) returns a new instance, showcasing the functional approach. This pattern is applied throughout, ensuring no in-place modifications occur.


329-337: Adding field dependencies
Appending dependencies via _dependencies.add preserves the original map’s state while returning a new instance. This is consistent and correct.


352-359: extend to add fields
Creating a new instance within extend ensures immutability. addAll(fields.toIMap()) merges extra fields without altering the existing map.


365-365: merge method
Deferring to extend simplifies logic by centralizing the merge implementation into one method.


376-383: pick returns new instance
Extracting a subset of fields from _fields and building a new AcanthisMap highlights a functional approach to data slicing.


394-401: omit returns new instance
Similar to pick, omitting fields yields an uncluttered approach to removing keys while maintaining immutability.


406-413: passthrough
Switching _passthrough to true in a new instance avoids side effects on existing map objects. This is exactly how immutable toggles should behave.


432-442: withAsyncCheck
Appending an async check to the operations list sets isAsync to true in the returned map. This design aligns with a purely functional approach to enabling async validations.


444-454: withCheck
Adding a synchronous check in a new map fosters a predictable and safe environment for future transformations or validations.


456-467: withTransformation
As with checks, transformations are appended to the operations list without mutating the original object. This seamlessly maintains an event chain for transformations.


472-472: object factory
Using .toIMap() ensures that the factory function produces an immutable version of the provided fields. This is consistent with the rest of the design.


475-475: Immutability annotation
Applying @immutable to the private _Dependency class is part of the overall immutability focus.


481-481: const _Dependency
By making the constructor constant, _Dependency instances also become compile-time constants, preventing runtime mutation.


487-491: const LazyEntry constructor
This ensures lazy evaluation logic is carried out under an immutable contract, preventing accidental modifications to _type or inherited fields.


505-512: withAsyncCheck in LazyEntry
Maintaining a consistent approach to isAsync and operations updates ensures that any lazy type is also fully immutable.


515-521: withCheck in LazyEntry
Similar to withAsyncCheck, the synchronous check is appended without direct mutation. This is a proper extension of the LazyEntry concept.


523-529: withTransformation in LazyEntry
The transformation is added to operations, returning another lazy entry instance. Each step remains purely functional.

@francescovallone francescovallone self-assigned this Apr 9, 2025
@francescovallone francescovallone self-requested a review April 9, 2025 08:16
@francescovallone francescovallone removed their assignment Apr 9, 2025
@francescovallone

Copy link
Copy Markdown
Collaborator

@dickermoshe So I totally agree with these changes, which would allow acanthis validators to be used as parameters in annotations. I am a little concerned about performance. Constantly reinstantiating an object could put a strain on the performance of the application and I think we should investigate this a bit more to avoid slowdowns due to these changes.

@dickermoshe

Copy link
Copy Markdown
Contributor Author

I'll write a benchmark for this.

Keep in mind that dart is purpose built to make instantiating classes very cheap. A nested widget tree with 1000s of widgets can easier be built in 8ms. Also everything here is const, so there are no memory copies when creating adding new Checks/Transformation.

Also, this only occurs when creating a validator, not when using it. So validating 10,000 would take the same long as a single one.

Also, all forms of functional programming use immutability, so this isn't some sorta hack that we're attempting.

However, in systems programming they avoid immutability for this very reason, however they need insane performance, work with with shared memory multi threading and are dealing with large quantities of memory.

@francescovallone

francescovallone commented Apr 9, 2025

Copy link
Copy Markdown
Collaborator

I'll write a benchmark for this.

Perfect thank you so much!

Keep in mind that dart is purpose built to make instantiating classes very cheap. A nested widget tree with 1000s of widgets can easier be built in 8ms. Also everything here is const, so there are no memory copies when creating adding new Checks/Transformation.

I'm aware of it, I just want to be sure that everything is still smooth. 😄

@dickermoshe

dickermoshe commented Apr 9, 2025

Copy link
Copy Markdown
Contributor Author

Benchmark

import 'package:acanthis/acanthis.dart' as acanthis;

void makeValidators() {
  final boolean = acanthis.boolean().isTrue().isFalse();
  final email = acanthis.string().email().min(5).max(20);
  final url = acanthis.string().url().min(5).max(20);
  final int = acanthis.number().integer();
  final double = acanthis.number().double();
  final date =
      acanthis.date().min(DateTime(2020, 1, 1)).max(DateTime(2023, 1, 1));
}

void main() {
  final stopwatch = Stopwatch()..start();
  for (final _ in List.generate(100000, (index) => index)) {
    makeValidators();
  }
  stopwatch.stop();
}

Hyperfine Results:

Benchmark 1: .\lib\new_version.exe
  Time (mean ± σ):      2.603 s ±  0.207 s    [User: 0.559 s, System: 0.133 s]
  Range (min … max):    2.261 s …  3.019 s    10 runs

Benchmark 2: .\lib\old_version.exe
  Time (mean ± σ):      2.388 s ±  0.173 s    [User: 0.770 s, System: 0.142 s]
  Range (min … max):    2.218 s …  2.672 s    10 runs

Summary
  .\lib\old_version.exe ran
    1.09 ± 0.12 times faster than .\lib\new_version.exe

Summary

It used to take 0.02388ms to run makeValidators. Now it takes 0.02603ms.
This in an tiny increase of 2.15µs. This is complete reasonable.

The next pr will make it quicker than it was originally.

@francescovallone

Copy link
Copy Markdown
Collaborator

I totally agree with you. I will proceed to merge the pull request. Thank you very much for your time and patience.

@francescovallone francescovallone merged commit 0c5c8a5 into avesbox:main Apr 9, 2025
@dickermoshe

Copy link
Copy Markdown
Contributor Author

I forgot to make fast_immutable_collections and meta more flexible.
I'll do that in the next PR

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.

2 participants