Arc Forumnew | comments | leaders | submitlogin
Shorter programs, fewer axioms
10 points by im 6159 days ago | 14 comments
1. I have never understood why I can't write (f . args) in place of (apply f args). It's not as if there is some other, better way to interpret a literal pair. If I could do this, it would render apply superfluous.

2. By having def and =, we're probably multiplying entities unnecessarily. A single, smarter primitive, like PLT-style define, would be simpler: we could write something like (= ((foo . args) (arg11 arg12 arg13) (arg21 arg22)) <code>) and have a function that takes any number of arguments, and returns a function that takes as arguments one list of exactly three things and one list of exactly two things.

3. If we're Huffman coding the language, & and | have a clear advantage over and/or. If we're going to have bitwise operators, && and || ought to be bitwise and/or. (C gets this backwards---boolean operations are far more common than bitwise.)

4. Scheme has the naming convention that methods changing state end in !. It may be possible to auto-generate a state-changing method from a functional one, for example sort! from sort. It would be interesting if ! were dealt with syntactically in this way---if appending ! to a function set its first argument to the retval of the function. The conversion to S-expressions is of course

(foo! x . args) -> (= x (foo x . args))



5 points by pg 6159 days ago | link

1 is not a bad idea. I may do this. But you'd still need apply for use as an argument.

2 wouldn't work because you couldn't redefine a var that was a hash table to be a fn.

4 is zap.

-----

1 point by im 6158 days ago | link

Re. 2: That's true. In general, it would mean you could only define names with =, not objects in general. The other option would be to leave = as is and change def to have PLT-esque behavior, but then we're away from the whole "reducing the number of operators" idea. (And whether (def f (args) ....) or (def (f args) ... is better is is kind of a religious debate, though my religion favors the second as more closely resembling how f is actually used later on.)

Re. 4: It might be zap, if you could write something like ((zap f) x y z) or (zap f x y z), instead of (= x (f x y z)). In fact, though, (f! x y z) is equivalent to (zap [f _ y z] x), and f! alone is equivalent to (fn args (= (car args) (apply f args))). (I couldn't think of a more concise expression using zap.) I think I'd much rather pass f! than (fn args (= (car args) (apply f args))) as an argument.

Actually, I might be mistaken, and I don't have the code (or my own machine) in front of me at the moment. Can you write (zap f x y z) right now?

-----

2 points by im 6157 days ago | link

After testing it, I find that (zap f x y z) does in fact act as (= x (f x y z)). And, if implicit currying is added as per someone else's suggestion, (zap f) will act as (fn args (= (car args) (apply f args))). So that works, then.

-----

2 points by vrk 6159 days ago | link

So point 4 would mean ! is a unary postfix operator, not too different from ~, the unary prefix Boolean negation. You can do this with the current macro system (code not tested):

  (mac ! (f fst . args)
     `(= ,fst (,f ,fst . ,@args)))

  ; Then:
  ((! foo) x . args)
The problem with this is that it's not very efficient. Sure, it's easy to define destructive functions this way from any given function, but for example sorting a list in-place is different from sorting a list, then substituting the value back to the original list. If this were implemented, there would be two different sorts, for instance; insort and sort!, and the latter were always the inefficient one (unless it was defined to be insort, which doesn't make sense in this case).

-----

4 points by im 6159 days ago | link

There are two possible resolutions if the inefficiency is too much to bear:

- Have a really, really smart interpreter that can often convert nondestructive algorithms to efficient in-place ones. This seems difficult.

- Overload foo!. That is, interpret foo! as either foo! itself if it is defined, or (lambda (x . args) (= x (foo s . args))) if it doesn't.

-----

3 points by pg 6159 days ago | link

zap is a lot like this.

-----

1 point by sacado 6159 days ago | link

The other problem is that it changes the global value of fst. It breaks if I want to modify something declared in a "let", for instance.

-----

4 points by cooldude127 6159 days ago | link

one problem with #4: the main purpose of a destructive implementation is that they can use a more efficient implementation by working in-place. This entire benefit is negated by just using the functional implementation.

-----

1 point by drcode 6159 days ago | link

I think the ! concept you outline has some appeal because it requires us to treat state change as a specific entity- Maybe then it would be possible to have a way to turn it on or off for a chunk of code in order to have enforced purely functional code (sorta like in Haskell, but at runtime)

I could envision something like:

  (= x '(2 5 3 4 1))

  (def foo ()
      (sort! < x))

  (w/out-mutation
         (foo))

  ***ERROR- FORBIDDEN EXTERNAL MUTATION IN FUNCTION (FOO)***
Of course, there would have to be an ability by the compiler to automatically substitute code using this construct to do something more efficient when mutation is permitted.

-----

2 points by bOR_ 6159 days ago | link

In ruby the lack of a ! at the end of a method isn't a guarantee that a function changes (caught me a few times by surprise). It is just used to distinguish between a state-changing version and nonstate-changing version. The reason for that was to limit the number of !! in your code.

  (+ counter 1)
would have to become

  (+! counter 1)

-----

2 points by lojic 6159 days ago | link

"In ruby the lack of a ! at the end of a method isn't a guarantee that a function changes"

Don't you have that backwards? Typically the ! suffix on a method name in Ruby indicates that the method modifies state.

Also, why would (+ counter 1) have to change to (+! counter 1) ? The former doesn't modify state, it simply returns the sum, right?

-----

1 point by bOR_ 6159 days ago | link

Ugh, as lojic pointed out (thanks for that :)), some sleep-deprived errors in this post. The more correct version:

  In ruby, the lack of a ! at the end of a method isn't a guarantee that a function doesn't modiefy state.
And you're right about (+ counter 1). Blindly remembered the example I got in the ruby forum when I was surprised that some method modified state without a warning !.

The correct example in arc would be this:

  (++ counter 1)
would have to become

  (++! counter 1)

-----

2 points by wfarr 6159 days ago | link

A couple weeks ago, I made a query about changing the spec to make destructive functions end in ! and functions that return boolean results end in ?. I still think this is a good idea.

(Which corresponds to OP's #4)

-----

1 point by binx 6159 days ago | link

1. Should (apply f '(1 2)) be written as (f . '(1 2)), that is, (f quote 1 2)?

2. It's like a kind of ad-hoc non-complete pattern matching. It would be better if we implement a complete pattern matching operator as a macro, in my opinion.

3. I have no idea whether it's right, but I would be glad if most special characters are left for the user who wants to write reader macros.

4. I have no idea.

-----