Arc Forumnew | comments | leaders | submitlogin
1 point by rocketnia 2745 days ago | link | parent

"I do wish there was an easier way to tell whether or not a value was provided as nil"

I share this sentiment. One thing we could do is have a space of hidden-from-the-programmer variables which tell you whether other variables have been bound. They can be accessed using a macro:

  (= given-prefix* (string (uniq) "-given-"))
  (mac given (var)
    ; NOTE: I don't think this will work properly for nil, but nil is
    ; never a local variable name anyway.
    (sym:+ given-prefix* var))
The implementation of argument lists would need to be aware of 'given-prefix* and bind the prefixed variables at the same time as the regular ones.

---

"Maybe doing destructuring on rest args would help solve that problem in most cases?"

What do you mean by that?



2 points by shader 2745 days ago | link

Well, if you use a rest arg for all optional values, and then use some form of destructuring bind on that list to extract your optional arguments, then you can tell whether or not they were passed in or merely defaulted to nil by just searching the arg list.

  (def test args
    (if (assoc 'c args)
          (pr "c was passed")
        (pr "c was not passed")))

-----

1 point by rocketnia 2744 days ago | link

I still don't follow. We can already manage the argument list manually, but in most of the suggestions here, we can only do it if we don't destructure it in the signature (unless we use more complicated kinds of destructuring).

  ; Current options:
  
  (def test args
    (let ((o c)) args
      (pr:if (len> args 0)
        "c was passed"
        "c was not passed")))
  
  (let missing list.nil  ; This is just something unique.
    (def test ((o c missing))
      (pr:if (is c missing)
        "c was not passed"
        "c was passed")))
  
  
  ; Some hypothetical options and non-options:
  
  (def test (& (c))
    (pr "no way to tell if c was passed"))
  
  (let missing list.nil
    (def test (& (c))
      (pr "still no way to tell if c was passed")))
  
  (def test (& args)
    (let ((o c)) args
      (pr:if (len> args 0)
        "c was passed"
        "c was not passed")))
  
  (def test (& (&both args (c)))  ; Destructure twice.
    (pr:if (len> args 0)
      "c was passed"
      "c was not passed"))
  
  (def test ((o c nil c-passed))
    (pr:if c-passed
      "c was passed"
      "c was not passed"))
  
  (def test ((o c))
    (pr:if given.c
      "c was passed"
      "c was not passed"))
  
  (def test (c)  ; Parameter lists are just destructuring.
    (pr:if given.c
      "c was passed"
      "c was not passed"))
  
  (def test (&both args (c))
    (pr:if (len> args 0)
      "c was passed"
      "c was not passed"))

-----

1 point by Pauan 2744 days ago | link

Brilliant! In fact, you could write a macro that would do that for you:

  (mac defreq (name args . body)
    `(w/uniq gen
       (def ,name ,(map (fn (x) `(o ,x gen)) args)
         ,@(map (fn (x) `(if (is ,x gen) (err:string "parameter " ',x " is required"))) args)
         ,@body)))

  (defreq foo (x y) (+ x y))
  (foo)     -> x is required
  (foo 1)   -> y is required
  (foo 1 2) -> 3
It probably breaks with rest arguments, but I think you could get those working too.

-----

1 point by Pauan 2744 days ago | link

Or this version, which is even better:

  (mac defreq (name vars . body)
    (if (isa vars 'cons)
          (let exp (len vars)
            `(def ,name args
               (let giv (len args)
                 (if (< giv ,exp)
                       (err:string "expected " ,exp " arguments (" giv " given)")
                     (apply (fn ,vars ,@body) args)))))
        `(def ,name ,vars ,@body)))


  (defreq foo (x y) (+ x y))
  (foo)     -> error: expected 2 arguments (0 given)
  (foo 1)   -> error: expected 2 arguments (1 given)
  (foo 1 2) -> 3
  
  (defreq foo args args)
  (foo)     -> ()
  (foo 1)   -> (1)
  (foo 1 2) -> (1 2)
  
It fails on functions that take required and rest args, though:

  (defreq foo (x y . args) (list x y args)) -> error
Err... right, you were talking about detecting if an argument was nil or not given... but I realized that the same technique could be used to write a version of def that implements required arguments even in a language where every argument is optional.

-----