tags:

views:

181

answers:

3

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!

+6  A: 

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 http://stackoverflow.com/questions/1315099/clojure-macro-to-create-a-synonym-for-a-function/1315233#1315233

Greg Harman
I was exploring the implementation of a feature like this, but I'm glad to know the quick way to do it too, thanks!
Evan Senter
+12  A: 

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 by defn) 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 called foo 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 of def & fn syntax. So defn is out of question for creating Vars with preexisting values (which might be functions), as is required for creating aliases; use defalias or simply def instead.

  • To create a variadic function, use the following syntax:

    (fn [x y & args] ...)
    

    x and y 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 name args. 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.), use apply:

    (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 possibly resolve / 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. :-)

Michał Marczyk
Michał Marczyk
Come on, dude. At least give other people a chance! Isn't there some sort of drug you can take to lower your intelligence level back to *normal*? Come back to us, bro, we miss you. :p
Rayne
Thanks for the in-depth comment. It definitely illuminated most all of what I was looking for. With regards to point #3 (vars are named by symbols, not keywords), is there a way to case a keyword into a symbol? Say, for the sake of an exercise I wanted to write a function that wrapped def, and took two arguments, a keyword and a value, and then internally called def and named the variable the keyword's name. Possible?
Evan Senter
Michał Marczyk
@Rayne: Thank you for your very kind overthetopism. I shall strive to become deserving of it. :-)
Michał Marczyk
+3  A: 

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)
j-g-faustus
Thanks for the synonyms. :]
Evan Senter