All, I'm starting to take a look at the Clojure language, and had a couple questions about something I'm trying to do. The broad objective is to alias the sequence function every?
to all?
. I'm sure there's a function or macro that does alias-ing (or something along those lines) but I wanted to see if it was possible with some of the basic constructs I know thus far. My approach was going to be to define a function called all?
that applies its arguments to the every?
implementation.
I'm curious to see if this can be made agnostic, so I wanted to parameter my alias function to take two arguments, the new name (as a Keyword) and the old name (as a function reference). In striving towards this goal, I've encountered two problems.
1) Defining named functions with Keywords throws errors. Apparently it wants clojure.lang.IObj
.
user=> (defn :foo "bar")
java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to clojure.lang.IObj (NO_SOURCE_FILE:0)
Is there a function to cast a Keyword to an IObj, or other means to parameterize the name of a newly defined function with some provided value? (In Ruby, define_method amongst other techniques does this)
irb(main)> self.class.instance_eval do
irb(main)* define_method(:foo) { "bar开发者_高级运维" }
irb(main)> end
=> #<Proc>
irb(main)> foo
=> "bar"
2) Collect all arguments to a function into a single variable. Even basic functions such as (+ 1 2 3 4)
take a variable amount of arguments. All the function definition techniques I've seen so far take a specific amount of arguments, with no way to just aggregate everything in a list for handling in the function body. Once again, what I'm going for is done in Ruby like so:
irb(main)> def foo(*args)
irb(main)> p args
irb(main)> end
=> nil
irb(main)> foo(1, 2, 3)
[1, 2, 3]
=> nil
Thanks for any help you can provide me!
I'll answer in bullet points, since the questions can be split neatly into a number of separate issues.
Something which is implicitly contained in what is to follow, but which perhaps warrants a bullet of its own: the top-level objects created by
def
& Co. (and in particular bydefn
) are Vars. So what you actually want to do is to alias a Var; functions are just regular values which don't really have names (except they may have a name bound to themselves locally inside their bodies; that's nothing to do with the issue at hand, though).There is indeed an "aliasing macro" available in Clojure --
clojure.contrib.def/defalias
:(use '[clojure.contrib.def :only [defalias]]) (defalias foo bar) ; => foo can now be used in place of bar
The advantage of this over
(def foo bar)
is that it copies over metadata (such as the docstring); it even appears to work with macros in the current HEAD, although I recall a bug which prevented that in earlier versions.Vars are named by symbols, not keywords. Symbol literals in Clojure (and other Lisps) do not start with colons (
:foo
is a keyword, not a symbol). Thus to define a function calledfoo
you should write(defn foo [...] ...)
defn
is a helper macro easing the creation of new function-holding Vars by allowing the programmer to use a mix ofdef
&fn
syntax. Sodefn
is out of question for creating Vars with preexisting values (which might be functions), as is required for creating aliases; usedefalias
or simplydef
instead.To create a variadic function, use the following syntax:
(fn [x y & args] ...)
x
andy
will be required positional arguments; the rest of the arguments passed to the function (any number of them) will be collected into a seq and available under the nameargs
. You don't have to specify any "required positional arguments" if they are not needed:(fn [& args] ...)
.To create a Var holding a variadic function, use
(defn foo [x y & args] ...)
To apply a function to some arguments you've got assembled into a seqable object (such as the
args
seq in the above examples or perhaps a vector &c.), useapply
:(defn all? [& args] (apply every? args))
If you want to write a function to create aliases -- as opposed to a macro -- you'll need to investigate the functions
intern
,with-meta
,meta
-- and possiblyresolve
/ns-resolve
, depending on whether the function is to accept symbols or Vars. I'll leave filling in the details as an exercise to the reader. :-)
All you need to do is bind the every? function to the all? symbol, which is done via def:
(def all? every?)
For a bit more on this, see Clojure macro to create a synonym for a function
Don't think I can add much to the existing explanations here, except perhaps fill out a couple of blanks in the Ruby traveller's dictionary on argument collection and destructuring:
(defn foo [& args] ; Ruby: def foo(*args)
(println args))
user=> (foo 1 2 3)
(1 2 3)
(defn foo [& args]
(+ args))
user=> (foo 1 2 3)
java.lang.ClassCastException ; + takes numbers, not a list
(defn foo [& args]
(apply + args)) ; apply: as Ruby proc.call(*args)
user=> (foo 1 2 3)
6
(defn foo [& args]
(let [[a b & other] args] ; Ruby: a, b, *other = args
(println a b other)))
user=> (foo 1 2 3)
1 2 (3)
精彩评论