Is it good practice for a Clojure record to implement IFn?
Asked Answered
M

2

16

Suppose I have a record that is "function-like", at least in the sense that it represents an operation that could be applied to some arguments.

I can make it work as a function by implementing clojure.lang.IFn, something like:

(defrecord Func [f x]
  clojure.lang.IFn
    (invoke [this arg]
      (f x arg))
    (applyTo [this args]
      (apply f x args)))

 ((->Func + 7) 1)
 => 8

(yes I know that I've just reimplemented an inferior version of partial.... it's just an example :-) )

Is making a record implement clojure.lang.IFn a good practice or not?

Any pitfalls to this approach?

Migratory answered 15/8, 2012 at 2:52 Comment(0)
T
6

I'm surprised it doesn't already. Records are supposed to be "a complete implementation of a persistent map". So to answer your question, I'd expect it to be a function of its keys, as a map is; anything else would be quite surprising.

Tragedian answered 15/8, 2012 at 5:24 Comment(3)
A record seems to implement clojure.lang.Associative but not clojure.lang.IFn. So you can do (:a some-record) to look up the keyword but not (some-record :x)Migratory
Precisely. I've checked but haven't seen anything explicit about this from Rich. Perhaps it's just an oversight.Tragedian
+1 given because @mikera's question asks about records not types. Because records have map-like semantics already it would be confusing, for types this problem isn't there.Pinnati
H
0

I can not give a direct Yes/No answer, but I can share my experience.

I defined a record implemented clojure.lang.IFn. Implementing IFn was to let me test it through REPL environment easily.

That record was intended to be a Job class, which was going to be processed by a worker. Therefore, I also implemented another interface java.lang.Runnable and a run function.

When I really put the code into integration test, it threw exception. Why?

The worker logic was something like this:

  1. It checked if the Job class is a Callable instance, if so, invoke the call function.
  2. It checked if the Job class is a Runnable instance, if so, invoke the run function.

However, clojure.lang.IFn has already extended Callable and Runnable, so the exception raised because I forgot to implement the call function.

Horseflesh answered 18/2, 2021 at 6:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.