match-lambda and variable arity?

40 views
Skip to first unread message

dun...@cogitat.io

unread,
May 13, 2013, 3:06:01 AM5/13/13
to lisp-flavo...@googlegroups.com
I've got a function that contains a match-lambda and my hope was that in the first clause I could have one arg and in the second, two args, etc., thus allowing me to dispatch based upon the number of args.

The function in question is here:
  https://github.com/oubiwann/ledis/blob/master/src/ledis.lfe#L41

You can see the entire context in that module. For the purposes of discussion, I'll paste the function here:

(defun make-client (host port database password)
  (let (((tuple 'ok client) (eredis-start host port database password)))
    (lambda (command)
      (match-lambda
        ((arg)
          (do-query client command arg))
        ((arg1 arg2)
          (do-query client command arg1 arg2))))))


Question 1:

Is is possible in to do what I want with match-lambda in LFE? Only the first clause seems to work...

> (slurp '"src/ledis.lfe")
> (set client (make-client))
> (get client '"fooz-4")
"barz-4"

> (set client '"fooz-4" '"beez-4")
exception error: #(badarity #(#Fun<lfe_eval.26.53503600> ("fooz-4" "beez-4")))


Question 2:

This function works in the REPL with slurp (as long as I pass it only one arg, as demonstrated in the example above). If, however, I try to compile, I get the following error:

> (c '"src/ledis")
src/ledis.lfe:41: head arity mismatch
error


Is this just because compilation introspects to a greater extent and finds the problem with the match-lambda, whereas the slurp behaves differently?

Thanks for any help!

d

rvirding

unread,
May 13, 2013, 11:14:05 AM5/13/13
to lisp-flavo...@googlegroups.com
In LFE one function definition (defun) only defines one function and each function has a fixed number of arguments. This is the same as for vanilla Erlang. The compiler is doing the "right thing" here in that it seriously checks the match-lambda definition. The interpreter, however, which is used when you slurp, is lazy and cheats and uses the number of args  in the first clause as the number of args for the match-lambda. This is why it works when you call it with one argument but not two. I will fix it so when we slurp we run lint on the code.

If the return value from make-client is always called with send then you could do (not tested):

(defun make-client (host port database password)
  (let (((tuple 'ok client) (eredis-start host port database password)))
    (lambda (command)
      (match-lambda
        (((list arg))
          (do-query client command arg))
        (((list arg1 arg2))

          (do-query client command arg1 arg2))))))

(defun send (client-maker command arg)
  (funcall (get-method client-maker command) (list arg)))

(defun send (client-maker command arg1 arg2)
  (funcall (get-method client-maker command) (list arg1 arg2)))

Or if you want to go even further, but still in the same way of list of args:

(defun make-client (host port database password)
  (let (((tuple 'ok client) (eredis-start host port database password)))
    (lambda (command)
      (lambda (args) (do-query client command args)))))

(defun do-query (client command args)
    (let (((tuple 'ok response) (eredis-query client (cons command args))))
      (format-response response)))

(defun send (client-maker command arg)
  (funcall (get-method client-maker command) (list arg)))

(defun send (client-maker command arg1 arg2)
  (funcall (get-method client-maker command) (list arg1 arg2)))

Again not tested. This is fun! What does it do?

Duncan McGreggor

unread,
May 13, 2013, 8:14:02 PM5/13/13
to lisp-flavo...@googlegroups.com
On Mon, May 13, 2013 at 8:14 AM, rvirding <rvir...@gmail.com> wrote:
In LFE one function definition (defun) only defines one function and each function has a fixed number of arguments. This is the same as for vanilla Erlang. The compiler is doing the "right thing" here in that it seriously checks the match-lambda definition. The interpreter, however, which is used when you slurp, is lazy and cheats and uses the number of args  in the first clause as the number of args for the match-lambda. This is why it works when you call it with one argument but not two. I will fix it so when we slurp we run lint on the code.

If the return value from make-client is always called with send then you could do (not tested):

(defun make-client (host port database password)
  (let (((tuple 'ok client) (eredis-start host port database password)))
    (lambda (command)
      (match-lambda
        (((list arg))
          (do-query client command arg))
        (((list arg1 arg2))

          (do-query client command arg1 arg2))))))

(defun send (client-maker command arg)
  (funcall (get-method client-maker command) (list arg)))

(defun send (client-maker command arg1 arg2)
  (funcall (get-method client-maker command) (list arg1 arg2)))

Ugh. You know, I was so locked into that one view of hoping to force multiple arity, that I didn't see the obvious -- thanks so much :-)

This, of course, worked like a charm!
 
Or if you want to go even further, but still in the same way of list of args:

(defun make-client (host port database password)
  (let (((tuple 'ok client) (eredis-start host port database password)))
    (lambda (command)
      (lambda (args) (do-query client command args)))))

(defun do-query (client command args)
    (let (((tuple 'ok response) (eredis-query client (cons command args))))
      (format-response response)))

(defun send (client-maker command arg)
  (funcall (get-method client-maker command) (list arg)))

(defun send (client-maker command arg1 arg2)
  (funcall (get-method client-maker command) (list arg1 arg2)))

Oh, I do like that one! I will have to try that out later tonight...
 
Again not tested. This is fun! What does it do?

More than anything, it's an experiment in building a client library in LFE. It's a wrapper of eredis, so still not built from scratch, but is allowing me to flex some LFE muscles without diving in over my head ;-)

Though only a wrapper, it does provide a simplified API for accessing Redis databases as compared to the eredis library.

As it gains more functionality, I'll let folks know...

Thanks again, Robert :-)

d
Reply all
Reply to author
Forward
0 new messages