Arc Forumnew | comments | leaders | submitlogin
Simple partial in Arc
4 points by d0m 5177 days ago | 9 comments
I've been thinking a lot recently about a way to add currying and partial into Arc.. and I know I'm not the only one. After pondering <>, {}, a new [], and even other crazy stuff, I came to the conclusion that it would be really hard to add a currying without breaking the simplicity of Arc.

However.. here's an idea: Why not make all Arc curry-able(?) by convention?

  (def-partial > (x y)
    (<= y x))

  (map (> 2) '(1 2 3 4 5)) -> (nil nil t t t)
And for some other cases of currying (not last position, more then one curry applied), we could still use our old [] friend:

  (map [> _ 2] '(1 2 3 4 5)) -> (nil nil t t t)
p.s. (< 1) returns t.. I guess it's because < is recursively defined and returns "t" when there is only one parameter. However, I think (< 1) returning (fn (x) (< 1 x) is way more useful then returning t.

What does (< 1) even mean? Is 1 smaller? Yes.



4 points by waterhouse 5177 days ago | link

I think the main problem with implicit currying is that it's basically incompatible with functions that take a variable number of arguments. E.g. should (+ a b) return a+b, or should it return (fn (c) (+ a b c))? Should (disp x) print x, or should it return (fn (out) (disp x out))? I think there is no satisfactory way for both concepts to coexist in a language.

You might just implement it for a couple of built-in functions to make them nicer (e.g. say that + or > called with one argument should probably curry rather than returning immediately) and not make it a general feature of the language, but I think that would be bad. For one thing, it wouldn't be nicer if your code contained, say, (apply + xs) when xs will have anywhere from 0-10 elements: nasty casework even if you are aware of this "feature".

Or you might say, "Well, we can certainly say that if a function f requires at least two arguments, then, when f is given only one argument, it should be curried." I think that's a bad idea too. It seems like a pretty rare case in the context of currying, and it makes things more complicated--now you have to figure out how many arguments f requires, instead of how many you intend to supply.

If you want implicit currying, I think you have to make it happen everywhere in the language, which means throwing out optional and rest parameters. (Interestingly, it does seem like it would work fine with keyword arguments.) Take a look at Haskell.

I prefer the way things are currently done. I don't find it a problem to write [> _ 4] instead of (<= 4) or something. I am annoyed whenever I have to write (fn args (apply f x args)), but if that becomes a problem, I'll write a partial macro to express it as (partial f x). ...Actually, it can just be a function.

  (def partial (f x)
    (fn args (apply f x args)))

  arc> (map (partial + 2) '(1 2 3 4))
  (3 4 5 6)
  arc> (map (partial (partial + 2) 3) '(1 2 3 4))
  (6 7 8 9)
See previous discussion as well (obtained with a Google search of "site:arclanguage.org currying").

http://arclanguage.org/item?id=3871

-----

2 points by rocketnia 5173 days ago | link

Interestingly, it does seem like [currying] would work fine with keyword arguments.

Very interesting observation! XD It's inspiring....

Well, CSS has the slickest keyword argument API I've ever seen. The declaration "border: 2px solid black;" conveys the same information as a whole lot of other declarations like "border-top-width: 2px;", "border-bottom-style: solid;", etc. What if the "border" keyword were definable as a library?

  ; Haskellish syntax with liberties taken
  
  border width style color f = f -: border-width width
                                 -: border-style style
                                 -: border-color color
  
  border-width width f = f -: border-top-width width
                           -: border-bottom-width width
                           -: border-left-width width
                           -: border-right-width width
  
  Type Border-Top-Width  ; singleton type
  
  border-top-width value f = f -: keyword Border-Top-Width value
The premise behind -: is that it's a low-precedence left-associative binary operator such that (a -: b) is just (b a). (I don't know if Haskell has something like that already....)

The "keyword" function is axiomatic to the language. It takes a key value, a value to associate with it, and a function, and it returns the result of currying that function with that keyword information, overwriting the previous association for that key if necessary.

As for defining a function that does something with the keyword arguments it gets this way... I'm out of steam for now. What do you think?

EDIT: Hmm. With currying, we consider two-argument functions to be indistinguishable from functions that return functions, right? If a programmer naturally wants to write a two-argument function that takes keyword arguments, should that be a keyword-taking function that returns a plain function, a plain function that returns a keyword-taking function, or what? What about keyword-taking functions that naturally have zero arguments? Maybe currying and keyword arguments aren't very compatible after all, or maybe they just need a different evaluation model. (Maybe keywords should be dynamic variables observed using an "I'll consume this whole subspace of keywords, thanks" syntax?)

-----

1 point by rocketnia 5177 days ago | link

Why not make all Arc curry-able(?) by convention?

I think that's the way to go if you like currying. Otherwise it's sort of a technique for peeling oranges in a language full of apples.

Nevertheless, I've thought about currying a bit myself, just 'cause it comes up on this forum so much. The following code is the culmination of where I usually end up, in my mind: http://gist.github.com/644503.

In a nutshell, the technique is to have functions keep track of some arity information so that they can be used in lisp-style calls as well as being transformable into curried versions. Using 'defcall in Anarki or Rainbow, we can define a new Arc type that behaves like a function but carries the necessary information.

Once my code is loaded, you can use >.2 and <.1 just as you'd like. I overwrite them explicitly so that they're of type 'curryable instead of 'fn, and you can do the same thing to other functions as desired, just as long as the currying code doesn't depend on them (so that you don't get infinite loops). You can also curry functions you haven't overwritten, using cur.3.union.is to describe the arity on a call-by-call basis.

  arc> (map >.2 '(1 2 3 4 5))
  (t nil nil nil nil)
  arc> (map <.1 '(1 2 3 4 5))
  (nil t t t t)
  arc> (map cur.2.apply.apply `((,+ (1 2 3)) (,* (1 2 3))))
  (6 6)
  arc> (map [apply apply _] `((,+ (1 2 3)) (,* (1 2 3))))  ; clearer?
  (6 6)
I've never bothered to put this into code before just 'cause I don't have a lot of oranges to deal with from day to day, but maybe it'll be useful for you. ^_^

-----

3 points by rocketnia 5177 days ago | link

Incidentally, I'm pretty sure (< x) has its current behavior so that (apply < lst) has a simple description: It always returns t if 'lst is increasing and nil if it isn't.

That being said, redefining '< this way isn't all that bad. The result of (apply < '(1)) ends up being a procedure, which still counts as a true value!

That being said, there may still be a problem if humans consistently have trouble simplifying (map >.2 '(1 2 3 4 5)). :-p

-----

1 point by akkartik 5177 days ago | link

Yeah it's an interesting question: Does currying help write programs much smaller than []?

I find myself rephrasing the question as: do we want special syntax for [] when _ occurs only in final position? Did you want something more powerful?

Hey, is your define-partial example correct? I think it should evaluate to

  (t t nil nil nil)
Am I misunderstanding your proposal?

-----

2 points by d0m 5177 days ago | link

wops, the second example should evaluate to (t t nil nil nil)

I thought about making [] more powerful.. but I can't find a way to do it the "arc way".

What I am proposing is: instead of creating a special construct for currying, why not make a convention that all arc functions be currying?

so simply: (< 2) evaluate to [< 2 _] .. No special suggar, just by convention.

And for the cases where we want more powerful currying technics, we can still use the [] syntax.

I.e.: [> _ 2]

-----

1 point by akkartik 5177 days ago | link

I'm sorry, I think I understand what you want, but the examples still aren't making sense:

You originally:

  (map (> 2) '(1 2 3 4 5)) -> (nil nil t t t)

  (map [> _ 2] '(1 2 3 4 5)) -> (nil nil t t t)
My response:

  (map (> 2) '(1 2 3 4 5)) -> (t t nil nil nil)

  (map [> _ 2] '(1 2 3 4 5)) -> (nil nil t t t)
Your response is this, am I right?

  (map (> 2) '(1 2 3 4 5)) -> (nil nil t t t)

  (map [> _ 2] '(1 2 3 4 5)) -> (t t nil nil nil)
But now both seem wrong. You used '<' sometimes; do you mean to use '<' everywhere?

Feel free to edit the original post to make the discussion clearer.

-----

1 point by d0m 5176 days ago | link

What I meant by "second example" was the "second map example". How can I edit the main post?

-----

1 point by akkartik 5176 days ago | link

Perhaps I see edit links longer because PG let me kill spam posts a while ago (they still time out eventually). Feel free to mail me the new version and I'll update the story (email in profile).

Update: Edit link is now gone.

-----