Huh? 😄
((fnil inc -1) :foo) ; => ":foo1"
((fnil inc 0) :foo) ; => ":foo1"
((fnil inc 666) :foo) ; => ":foo1"
((fnil inc "bar") :foo) ; => ":foo1"
I think OCaml has the least annoying type system of any strongly and statically typed language I've ever used, but ymmv, or whatever
1.11.132, by the way.
Oh, and:
(inc :foo)
; => ------ WARNING - :invalid-arithmetic ----------------------------------------------
; Resource: <eval>:1:1
; cljs.core/+, all arguments must be numbers, got [cljs.core/Keyword number] instead
; ----------------------------------------------------------------------------------- this is just testing that nope(+ x) = x for all x i think
That's expected due to how the underlying JS works.
The warnings comes from the compiler when it has type info available. fnil erases type info.
Clojure (1.12.0) is more in line with expectations:
((fnil inc -1) :foo)
; =>
; Execution error (ClassCastException) at sx.clj.statistics.dbops/eval162409 (REPL:484).
; class clojure.lang.Keyword cannot be cast to class java.lang.Number (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')Because Java is strongly typed and JavaScript is weakly typed. When trying to add something that's not a number to a number, both of the arguments get converted to a string and are the concatenated. Blame JS, not CLJS. :)
> Blame JS, not CLJS Oh, happily! 😆
Shame that these js* type hints get dropped by fnil...
(core/defmacro ^::ana/numeric +
([] 0)
([x] (core/list 'js* "(~{})" x))
([x y] (core/list 'js* "(~{} + ~{})" x y))
([x y & more] `(+ (+ ~x ~y) ~@more)))
; ...
(defn inc
"Returns a number one greater than num."
[x] (cljs.core/+ x 1))
; ...
(defn fnil
"Takes a function f, and returns a function that calls f, replacing
a nil first argument to f with the supplied value x. Higher arity
versions can replace arguments in the second and third
positions (y, z). Note that the function f can take any number of
arguments, not just the one(s) being nil-patched."
([f x]
(fn
([a] (f (if (nil? a) x a)))
([a b] (f (if (nil? a) x a) b))
([a b c] (f (if (nil? a) x a) b c))
([a b c & ds] (apply f (if (nil? a) x a) b c ds))))
([f x y]
(fn
([a b] (f (if (nil? a) x a) (if (nil? b) y b)))
([a b c] (f (if (nil? a) x a) (if (nil? b) y b) c))
([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) c ds))))
([f x y z]
(fn
([a b] (f (if (nil? a) x a) (if (nil? b) y b)))
([a b c] (f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c)))
([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c) ds)))))
I've thought about this for all of two seconds of course, but couldn't fnil be made to preserve them?I think they get dropped by every higher-order function. So in order to make it effective, you'd have to change everything. And then your own higher-order functions will be a problem. Or not even higher-order ones - a regular function that returns, say, a number, but does so in a way that escapes type inference, will be typed as basically any?. So in order to make it work across the whole or even most of the code, everybody would have to tag every function they write. Or the type inference machinery has to become so clever that it just knows everything, but I'm pretty sure it's not something anyone has figured out yet.
Hmm, right. I'd not want to open that can of worms either.
lol you've described Ocaml, which is both as cool and as annoying as you might imagine from that description