beginners

Colton Goates 2025-10-11T01:24:38.952269Z

I have a question about spec'ing higher order functions I have a function until that's defined as follows:

(defn until
  "Iterates until predicate is met"
  [p f x]
  (some #(when (p %)
           %)
        (iterate f x)))
My function spec is as follows:
(s/fdef until
  :args (s/cat :p (s/fspec :args (s/cat :x any?)
                           :ret boolean?)
               :f (s/fspec :args (s/cat :x any?)
                           :ret any?)
               :x any?)
  :ret any?)
When I call (until #(= 5 %) inc 0) is says the function call does not meet the spec, giving an error messages that says: (nil) - failed: Cannot invoke "Object.getClass()" because "x" is null in: [1] at: [:f]. Anyone know why?

Colton Goates 2025-10-11T20:52:02.393649Z

@didibus I actually ran a property test on until that seems to be why it was passing nil in. @hiredman That all makes a lot of sense. I was conceptualizing any? like a type system, but it's actually very hard to find a function that accepts anything as an argument; inc doesn't. It seems like it's hard to spec a highly generic function, right? Am I missing something?

2025-10-11T21:03:15.112879Z

It depends, if you want to use instrument, then I think fn? might be preferable to fspec, but there are other things you might use specs for where it wouldn't matter as much

👍 1
2025-10-11T02:03:40.298209Z

Don't :args (s/cat :x any?) means it needs an argument x, but you are passing inc with no args to it?

Colton Goates 2025-10-11T02:11:13.122159Z

I'm not sure. I thought a unary function would satisfy the spec. How would you make the call? Like (until #(= 5 %) #(inc %) 0)?

Colton Goates 2025-10-11T02:13:54.974149Z

That seemed to work... interesting

Colton Goates 2025-10-11T02:14:31.687199Z

Actually, I don't think it did... one sec

Colton Goates 2025-10-11T02:17:08.231249Z

Yeah, that didn't work

2025-10-11T02:19:29.682129Z

Because the way spec checks that a function is valid for a function spec is by using the function spec to generate arguments and call the function

2025-10-11T02:19:54.030609Z

The "smallest" value that any? generates is nil

Colton Goates 2025-10-11T02:21:48.849169Z

Oh, I see. should I use some? instead?

2025-10-11T02:23:11.055449Z

It doesn't matter, when you instrument a function and the spec for that function specs one of its args as a function that is how it works

Colton Goates 2025-10-11T02:25:32.495749Z

Okay, do you have a recommendation on how to rewrite the spec to avoid the null error?

2025-10-11T02:26:49.166949Z

You can't without sacrificing the generality (replacing any? with int?)

2025-10-11T02:28:37.352369Z

You could maybe replace fspec with fn? or ifn?, not sure if those have generators though

2025-10-11T04:10:35.033899Z

Right, inc is not in fact a function of any? to any? which is why it fails.

2025-10-11T05:26:17.099539Z

Though, I'm still unclear, I'm guessing you have used s/instrument. That would not run a generator over inc, it should just wrap inc in a way that it validates the args passed to it. So I'm not sure where that nil comes from.

2025-10-11T05:28:23.902569Z

until will be wrapped and try to validate that functions passed into it satisfy the specs

🤘 1
2025-10-11T05:33:04.527969Z

That's some wild slack-fu, I guess I had forgotten all about that. How big of a generation run does it do? Seems it would be pretty slow.

2025-10-11T05:38:01.558919Z

Ok, it's control by that flag clojure.spec.alpha/*fspec-iterations* and defaults to 21. Found the issue: https://ask.clojure.org/index.php/3818/disable-fspec-validation-during-instrumentation I personally would have assumed what Alex suggests: > Another option that has been proposed for this is to make the instrumented function also wrap the function arg in instrumentation according to its spec.

2025-10-11T05:49:42.332909Z

Best you just use fn? in your case then. And if you really want 1-ary? maybe you can do:

(defn unary-fn? [f]
 (and (fn? f)
  (try
   (f ::sentinel)
   true
    (catch clojure.lang.ArityException _ false)
    (catch Exception _ true))))
But feels a bit hacky, in that it has the same issue as what the ask says, which is it will execute the function on valiation. But also maybe it's fine since it seems fspec also does that.