Skip to content

feat(stdlib): add threading, conditional, and monadic macros#1545

Open
sqrew wants to merge 3 commits into
carp-lang:masterfrom
sqrew:stdlib-control-flow-macro-additions
Open

feat(stdlib): add threading, conditional, and monadic macros#1545
sqrew wants to merge 3 commits into
carp-lang:masterfrom
sqrew:stdlib-control-flow-macro-additions

Conversation

@sqrew
Copy link
Copy Markdown
Contributor

@sqrew sqrew commented Apr 22, 2026

This PR introduces a suite of powerful control-flow macros to the Carp standard library, enhancing ergonomics for data threading, conditional logic, and monadic operations.

Changes:

  • as-> / as->>: Flexible threading macros that name the value, enabling its use in any argument position.
  • cond-let: A multibranch conditional for Maybe bindings, providing clean unwrapping for multiple options.
  • cond-> / cond->>: Conditional threading macros that apply transformations only when their corresponding tests are true.
  • maybe-and-then-> / result-and-then->: Advanced monadic chaining macros for steps that themselves return Maybe or Result.
  • Tests: Integrated 11 new test cases into test/control_macros.carp.

These macros significantly improve the ergonomics of complex data pipelines and conditional logic.


Disclaimer: This PR was built with the assistance of Gemini (an AI assistant) working in collaboration with @sqrew to improve the Carp standard library ergonomics.

sqrew added 2 commits April 21, 2026 11:57
- Added as-> threading macro for more flexible piping.
- Added cond-let for multibranch conditional with Maybe bindings.
- Integrated tests into test/control_macros.carp.
… macros

- Added as-> and as->> for flexible named threading (first and last).
- Added cond-let for multibranch conditional with Maybe bindings.
- Added cond-> and cond->> for conditional threading.
- Added some-> and some->> for monadic threading with short-circuiting.
- Added maybe-and-then-> and result-and-then-> for complex monadic chaining.
- Integrated comprehensive tests into test/control_macros.carp.
Comment thread core/ControlMacros.carp Outdated
Each form in `steps` must return a value, not a Maybe.")
(defmacro some-> [val :rest steps]
`(match %val
(Maybe.Just x) (Maybe.Just (-> x %@steps))
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.

Shouldn’t this call some-> here?

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.

Also can we gensym here to avoid the hygiene leak with x?

Comment thread core/ControlMacros.carp Outdated
Each form in `steps` must return a value, not a Maybe.")
(defmacro some->> [val :rest steps]
`(match %val
(Maybe.Just x) (Maybe.Just (--> x %@steps))
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.

Shouldn’t this call some->> here?

Comment thread core/ControlMacros.carp Outdated
acc
(maybe-and-then-internal
`(match %acc
(Maybe.Just x) (-> x %(car steps))
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.

Can we gensym here too?

Comment thread core/ControlMacros.carp Outdated
acc
(result-and-then-internal
`(match %acc
(Result.Success x) (-> x %(car steps))
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.

And here also a gensym? (And the e below)

Comment thread core/ControlMacros.carp

(doc cond-> "Conditional threading macro (first).
Threads `val` through each `form` in `steps` only if the corresponding `test` is true.")
(defmacro cond-> [val :rest steps]
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.

We should check the numbers of forms is even here.

Comment thread core/ControlMacros.carp Outdated
(hidden cond-let-internal)
(defndynamic cond-let-internal [xs]
(if (= (length xs) 0)
(list)
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.

This restricts the typing. It should be a Maybe.Nothing, right?

…to avoid duplication, even len check on cond->/cond->>, cond-let empty returns maybe.nothing, added test
@sqrew
Copy link
Copy Markdown
Contributor Author

sqrew commented May 5, 2026

One thing I noticed: I use -> and ->> whereas the stdlib uses both -> and --> for first and last threading. I wonder if I should alter these to follow the same convention. Anyone have input on this?

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