Arc Forumnew | comments | leaders | submitlogin
1 point by fallintothis 5505 days ago | link | parent

Interesting idea, but I'm having some trouble seeing the consistency -- probably since I've never used PicoLisp. Please feel free to blind me with science, because my brain's not being a team player right now. (I suppose I could just, y'know, install PicoLisp, but asking dumb questions is much more fun.)

Arc has a handful of special forms (quote, quasiquote, if, fn, assign), each there because they specifically have less-than-normal evaluation rules (though at least quasiquote can be implemented as a macro: http://arclanguage.org/item?id=9962). How does PicoLisp handle expressions like the following (pseudo-)Arc code?

  (map if '(t nil) '((pr "a") (pr "b")))
My first thought is that it might correspond to

  (list (if 't   '(pr "a"))
        (if 'nil '(pr "b"))) ; => (list '(pr "a") nil)
which makes sense if if is supposed to be a "normal" function. But this hardly seems useful, since the whole point of if is to conditionally evaluate one of its branches. So, my next thought is that perhaps it's the same as

  (list (if t   (pr "a"))
        (if nil (pr "b"))) ; => prints "a" and returns (list "a" nil)
but why would I want if evaluating the arguments it's passed? It certainly shouldn't evaluate every argument, otherwise it'd be executing each branch (e.g., (if (signed treaty) (world-peace) (unleash-the-nukes))). So then, if this is the case, it's just evaluating some of its arguments? So if I wanted to return '(pr "a"), would I need to do something akin to the following?

  (if something
      ''(pr "a"))
Or is the whole idea of "everything is a function, even special forms" just kind of a pun? How do you start dealing with the other cases, like assign and quote (and fn I suppose, which is just quote in PicoLisp)?

Is the idea just that it'd be cool to pass around special forms as first-class objects? This is a well-tread path, in some ways, since the topic of first-class macros comes up every now and then (http://www.google.com/search?q=site:arclanguage.org+first-cl...).

If anyone could enlighten me, I'd appreciate it. (Read: make mine brain am thinking goodly!)



1 point by diiq 5502 days ago | link

Your first thought is correct.

    (map if '(t nil) '((pr "a") (pr "b")))
returns

    ((pr "b"))
because you've quoted the arguments. This is consistent; if you quote the arguments to if, you will get the same result (as you pointed out). Why would you expect a quoted argument to be evaluated?

The point is, just as you say, that you can pass functions which are normally considered 'special' and they will behave like any other function. Yeah, it's been done before (though you'll notice that picolisp comes up pretty often in your Google search; it's a canonical example) --- but that doesn't make it a bad idea; it's one less thing for the user to keep track of. I watched a person give up learning CL when they tried to pass a special form and discovered, after much frustration, that it was impossible.

-----

1 point by fallintothis 5499 days ago | link

This is consistent [...] Why would you expect a quoted argument to be evaluated?

I misspoke regarding consistency. What I meant was that the "useful" way (conditionally evaluating the branches) would be inconsistent, but the consistent way doesn't seem very useful (because the point of special forms is to behave specially).

Yeah, it's been done before [...] but that doesn't make it a bad idea

Certainly not! I didn't mean to imply first-class macros/special forms are a bad idea --- let alone just because they've been done before. I meant that the ideas are similar; since there's been more discussion about first-class macros, it might be useful to ponder those.

Even so, first-class special forms seem less promising than first-class macros. rntz already pointed out that functions can mimic PicoLisp's behavior for special forms, but you can even mimic the "useful" behavior for certain cases of if:

  arc> (def iffn (x y z) (if (eval x) (eval y) (eval z)))
  #<procedure: iffn>
  arc> (iffn '(prn "test") '(prn "then") '(prn "else"))
  test
  then
  "then"
  arc> (iffn '(do1 nil (prn "test")) '(prn "then") '(prn "else"))
  test
  else
  "else"
But that's still kind of a joke. And what about ones that don't even evaluate their arguments, like fn? If

  (car:map if '(t) '((pr "a")))
corresponds to

  (if 't '(pr "a"))
then does

  (car:map fn '(t) '((pr "a")))
correspond to

  (fn 't '(pr "a"))
which isn't even valid syntax? (At least by the way Arc's quote and fn interact; I understand PicoLisp's quote and fn are unified to just quote.)

I'm not against first-class special forms iff we can reason about them. It's just that reasonable interpretations look like solutions in search of the problem.

-----

1 point by rntz 5501 days ago | link

It's consistent, but it means that passing special forms to higher-order functions is essentially just a form of punnery - it lends you no more expressiveness. There is no way (unless I'm mistaken) to get (map if '(t nil) X), where X is some expression, to evaluate the first element of X (or its evaluation) but not the second. So I could as well just define a function:

    (def iffn a
      (iflet (c . a) a
        (iflet (x . a) a
          (if c x (apply iffn a))
          c)))
    
    (map iffn '(t nil) '(1 2) '(5 6))
    => (1 6)

-----