0

I've put together a higher order function that in certain cases calls a function parameter, but it seems to have different effects depending on the function. I was able to reproduce the same behaviour just with a simple function:

(defn foo [f a b] (f a b))

For "normal" functions it works fine:

user=> (foo list 2 3)
(2 3)
user=> (foo cons 1 '(2 3))
(1 2 3)
user=> (foo println 2 3)
2 3
nil

But for operators, it does not, it just seems to return the last element:

user=> (foo '+ 2 3)
3
user=> (foo '* 2 3)
3
user=> (foo '- 2 3)
3

Why is this the case?

3
  • Just as a fun fact of why "it just seems to return the last element", see here (the second arg given is a default, which it's defaulting to). I don't know why you'd need to do this, but it lets you do something like ('+ {'+ 2}) returns 2. Commented Sep 7, 2019 at 0:03
  • @Carcigenicate For the same reason you'd want to do it for keywords, of course. Sometimes you might just have a map keyed by symbol for whatever reason (e.g., the lexical environment when implementing an interpreter), and it's not crazy to want to be able to use symbols as functions to look themselves up. Commented Sep 7, 2019 at 1:55
  • @amalloy That's true. I just don't think I've ever had the need to do a lookup like that. Commented Sep 7, 2019 at 1:59

2 Answers 2

6
user=> (foo '+ 2 3)
3

Why is this the case?

' (or quote) is creating a symbol of + when you want the + function value itself: https://clojure.org/guides/weird_characters#_quote

(quote foo) => foo ;; symbol
'foo => foo ;; symbol

So the behavior of always returning the second argument comes from the fact that symbols (like keywords) also act as functions, typically used as a shorthand for get on associative structures (like maps), so these are functionally equivalent:

('foo 1 2) => 2
(get 1 'foo 2) => 2

The 2 happens to be in the position used for default values when the key isn't found in the associative structure.

This would be useful if you had a map with symbol keys, just like keywords:

('foo {'foo 1}) => 1
({'foo 1} 'foo) => 1
('foo {'bar 1} 2) => 2
Sign up to request clarification or add additional context in comments.

1 Comment

Crazy! I have never heard of this. I would consider it a "gotcha" instead of a "feature".
1

In clojure, "operators" like + are just normal functions. Don't use the single-quote and it'll work fine.

(ns tst.demo.core
  (:use tupelo.core tupelo.test))

(defn foo [f a b] (f a b))

(dotest
  (spyx (foo list 2 3))
  (spyx (foo println 2 3))
  (spyx (foo + 2 3))
  (spyx (foo * 2 3))
  (spyx (foo - 2 3)) )

with results:

(foo list 2 3) => (2 3)

2 3                        ; result of (println 2 3)
(foo println 2 3) => nil   ; println always returns `nil`

(foo + 2 3) => 5
(foo * 2 3) => 6
(foo - 2 3) => -1

The helper function spyx just prints an expression, then its value.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.