Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

condp (cond using predicate) ? #138

Open
mmontone opened this issue Aug 22, 2022 · 7 comments
Open

condp (cond using predicate) ? #138

mmontone opened this issue Aug 22, 2022 · 7 comments

Comments

@mmontone
Copy link

I recently had a look at Clojure's condp: https://clojuredocs.org/clojure.core/condp
and I thought it could be handy for CL too, simplifies code.

Here is a slightly different implementation:

(defmacro condp (predicate &body clauses)
  "COND using PREDICATE."
  (let ((pred (gensym)))
    `(let ((,pred ,predicate))
       (cond
	 ,@(loop for clause in clauses
		 collect `((funcall ,pred ,(car clause))
			   ,@(cdr clause)))))))

For example, this:

(defun object-kind (obj)
  (cond
    ((typep obj 'number) "number")
    ((typep obj 'string) "string")
    ((typep obj 'hashtable) "map")
    ((typep obj 'package) "module")
    ((typep obj 'class) "type")))

can be rewritten as:

(defun object-kind (obj)
  (condp (alexandria:curry #'typep obj)
    ('number "number")
    ('string "string")
    ('boolean "boolean")
    ('hash-table "map")
    ('package "module")
    ('class "type")))

Is there an idiom for this in CL or Serapeum already? Do you think this is worth including?

@mmontone
Copy link
Author

mmontone commented Jul 24, 2023

What I like about this construct is that you can encode "custom pattern matchers" with it, and the resulting code is nice to read.

For example, a regex matcher:

(condp (alexandria:rcurry #'ppcre:scan "my-string")
   ("^foo" :foo)
   ("^bar" :bar)
   ("^my.*" :mine)
   ("^mi.*" :mio))

Or a string matcher:

(condp (alexandria:curry #'string= "some")
   ("foo" :foo)
   ("bar" :bar)
   ("some" :some))

(Now I'm thinking it could also be called match-with or similar).

What do you say @ruricolist ? Not interested?

@ruricolist
Copy link
Owner

I think this would be nice to have. I like condp better as a name; it arguably makes more sense in CL (with our -p predicates) than it does in Clojure.

@ruricolist
Copy link
Owner

Is there a reason not to have a separate expression argument, like Clojure does?

@mmontone
Copy link
Author

mmontone commented Jul 25, 2023

Is there a reason not to have a separate expression argument, like Clojure does?

The reason is that I can control the order of arguments to the predicate using curry or rcurry as in the examples, and at which argument position the expression argument being tested is passed.
I'm not sure how the Clojure version let's you control that.
If you know, please let me know. I'll have a closer look.

@ruricolist
Copy link
Owner

ruricolist commented Jul 25, 2023 via email

@mmontone
Copy link
Author

I thought about that, but by composing a predicate without a separate expression, I have complete control over how I can compose the predicate, and which arguments doesn't matter how many and where to pass them. And so I didn't see the point on separating predicate and expression.

@mmontone
Copy link
Author

With the most extreme case of using a lambda:

(condp (lambda (option) (string= option "my expression"))
     ("lala" :no)
     ("my expression" :yes))

I suppose it is a question of readability, as the predicate-only option is very flexible.

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

No branches or pull requests

2 participants