Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anonymous function literals. #879

Closed
gilch opened this issue Aug 9, 2015 · 32 comments
Closed

Anonymous function literals. #879

gilch opened this issue Aug 9, 2015 · 32 comments

Comments

@gilch
Copy link
Member

gilch commented Aug 9, 2015

Is there any interest in adding something like Clojure's anonymous function literals (e.g. #(+ %1 %2 %&)) in Hy? I've got a working macro for my own use, so it wouldn't be hard for me to add it to Hy.

We may want to preserve the #( reader macro for genexprs per #867. Possible alternatives are a reader macro % like #%(+ %1 %2 %&) or an ordinary macro ,\ like (,\ + ,1 ,2 ,&).

If you want it, would you prefer I put it in core or contrib?

@refi64
Copy link
Contributor

refi64 commented Aug 9, 2015

I like the idea...

...but doesn't Hy already have enough syntactic sugar (e.g. lambda vs fn, 'defunvsdefn`, etc.)?

@gilch
Copy link
Member Author

gilch commented Aug 9, 2015

I wouldn't call that sugar so much as pointless aliasing, which I was hoping to get rid of per #240. Maybe I should just make a pull request for that to get the ball rolling. Removing stuff seems a lot easier than adding it.

@refi64
Copy link
Contributor

refi64 commented Aug 9, 2015

Removing stuff seems a lot easier than adding it.

Not really. Once you add it, there's a good chance that removing it will break code. Of course, Hy is still in beta, so code breakage is "technically" ok, but it should still be avoided when possible.

@refi64
Copy link
Contributor

refi64 commented Aug 9, 2015

Also, one person said this about Clojure's lambdas:

As much as I love Clojure I have to say, that’s an ugly lambda. It looks like a child mashing the top row of the keyboard.

:/

@gilch
Copy link
Member Author

gilch commented Aug 10, 2015

After playing with Iverson's J, Clojure's anonymous function literal doesn't look so bad to me. But the example you linked to isn't a fair comparison, Clojure's equivalent lambda actually looks like this:

(fn [x] (+ 1 x))

The #() anonymous function literals are just an abbreviated syntax that lets you omit the argument list and a pair of parentheses. It can't replace the true lambdas (fn). You can't even nest them.

Is this version (,\ + ,1 ,2 ,&) less ugly? I'm not sure of your aesthetic here, but I can change most of it pretty easily.

@algernon
Copy link
Member

,\ looks a bit weird to me. ,# or #, (the latter may clash with reader macros, mind you) looks a bit better. But I have no strong preference either way.

@agentultra
Copy link
Member

Why, oh why, do we need syntax for anonymous functions? lambda is perfectly fine and legible.

From one perspective I understand that “lambda,” is so common that like “quote,” “unquote,” and friends it perhaps deserves syntax in order to keep readers from being distracted by mundane details. However the same could be said for “defn” or “defmacro” and others. Unlike “quote” and “unquote” I think “lambda” isn’t just noise but like “defn” and “defmacro” it defines a new form and deserves to be spelled out.

If it’s too much to type, write a reader macro or learn to use your text editor.

On Aug 10, 2015, at 2:15 AM, Gergely Nagy [email protected] wrote:

,\ looks a bit weird to me. ,# or #, (the latter may clash with reader macros, mind you) looks a bit better. But I have no strong preference either way.


Reply to this email directly or view it on GitHub.

@algernon
Copy link
Member

Being in the middle of the Grand Language Cleanup, I like @agentultra's suggestion best. 😆

@gilch
Copy link
Member Author

gilch commented Aug 10, 2015

,\ looks a bit weird to me.

It's not unusual for a programming language to substitute ASCII digraphs for other characters not easy to type, so becomes ->, for example. Similarly, λ could become ,\, or perhaps .\. Haskell doesn't even bother with a digraph and just uses \ for lambda. That's really the only reason I chose ,\. The choice of characters is not that important, and easily changed. It just has to be short.

Why, oh why, do we need syntax for anonymous functions? lambda is perfectly fine and legible.

Why, oh why, do we need a Hy? Python is perfectly fine and legible ;) "Need" is a relative term. One may as well ask, "Why does Clojure need #(...) when it has the perfectly fine and legible (fn [...] (...))?". For something so "unnecessary", it sure seems to get used a lot. The #(...) syntax is different from fn in that it doesn't need extra () in the body and does not require a parameter list. It's implied by special names for the parameters themselves.

A major use case for this syntax is partial function application with a minimum of ceremony. This is something one uses quite a lot when writing code in the functional style, especially in a language that does not have automatic currying. This is so important that Python added functools.partial, despite already having the perfectly fine and legible lambda that could easily be used for this purpose. See PEP 0309, for exactly this argument.

If it’s too much to type, ... learn to use your text editor.

Yes, and Java's tedious verbosity is made much more tolerable with a good IDE. Any language you have to excuse via editor has serious issues. A good Lisp should need little more than parentheses-balancing and indent support.

I'm surprised by the lack of enthusiasm here. I do understand (and support) the need to guard against cruft in the language, but this can be taken too far in the other direction. After all, Hy is a Lisp, and if some things are missing in the language, the user will add them. With macros, if necessary. (Like how I just added a macro for function literals.)

If a standard is too minimal for too long (e.g. Scheme, Lua), then the community will fragment because everybody has to re-invent the wheel, but they all do it independently and in incompatible ways. Hy avoids a lot of this kind of problem by supporting Python's libraries, just as Clojure does with Java's. But Clojure still has both fn and #(), and a large core (~600 forms vs Python's 68 builtins), because Java is not a Lisp, and can't help with that part. Python is not a Lisp either. Domain-specific macros will be in Hy libraries, or individual applications. That can't be helped. But the general-purpose macros that might otherwise get re-invented a lot belong in Hy proper, to combat fragmentation.

@refi64
Copy link
Contributor

refi64 commented Aug 10, 2015

I just feel like you save just a few characters for little benefit, and it's harder than it looks to compile. You can't just recurse through the AST:

(,\ (x ,1 (,\ ,2))) ; (fn [a] (x a (fn [_ b] b)))
(,\ (,\ (,\ ,3))) ; (fn [] (fn [] (fn [x] x)))

Also:

Yes, and Java's tedious verbosity is made much more tolerable with a good IDE.

Very true (I hate Java :). However, then you also have the APL extreme, which produces an insanely weird programs, like (in K):

/ reads a number from input and prints its factorial
`0:$*/1+!0$0:`

Language design is all about compromise, really.

@tuturto
Copy link
Contributor

tuturto commented Aug 10, 2015

How would people feel about λ for this (and then something nice looking for parameters)?

@farhaven
Copy link
Contributor

I'd be okay with λ, but only because my editor shows (fn) as (λ) anyway. I think we'd still need a way to enter function literals for people who don't have λ on their keyboard or a character map stashed somewhere. Something like this:

=> (def x #f(+ %1 %2))
; similar:
=> (def x λ(+ %1 %2))
=> (x 2 3)
5

Now that I'm thinking of it, if we do indeed go down the "make it a unicode symbol"-path: what do math people use to denote a parameter? My first intuition would be something like λ1, λ2 (think eigenvalues) or μ1, μ2, etc.

@gilch
Copy link
Member Author

gilch commented Aug 10, 2015

I just feel like you save just a few characters for little benefit, and it's harder than it looks to compile.

They don't support nesting! Neither does Clojure's. This was never meant to replace lambdas altogether. And I have a working macro. It might be improved by error checking, but it does work.

@agentultra
Copy link
Member

It’s entirely possible to use Java without an IDE. You just wouldn’t want to. I could use an editor as you described for Lisp development but I wouldn’t want to when I have emacs + slime + paredit. The bar is much higher than you think.

Java’s legendary verbosity is the least of its problems. Text editors take care of managing text for you. The good ones do anyway.

The problem I have with introducing Clojure’s syntax for LAMBDA is that I find it superfluous for the following reasons:

  1. Python users already type “lambda” (or have their editor do it for them)
  2. CL, Scheme, Racket, and innumerable other Lisps use “lambda"
  3. Like DEFN and DEFMACRO, LAMBDA introduces a new form. In contrast QUOTE and friends are only interesting to READ and are only moderate interest to a human reader — ergo reader macros such as ' and , in CL or Scheme.
  4. Parsers would have to be written to support structural editing and highlighting of short-hand lambda syntax (hy-mode in Emacs land for example)
  5. Special syntax requires beginners to become experts and is non-transferable rote knowledge.
  6. In the case of Hy there is no real benefit other than forcing new users to learn more syntax.

On Aug 10, 2015, at 2:23 PM, Matthew Egan Odendahl [email protected] wrote:

,\ looks a bit weird to me.

It's not unusual for a programming language to substitute ASCII digraphs for other characters not easy to type, so → becomes ->, for example. Similarly, λ could become ,, or perhaps .. Haskell doesn't even bother with a digraph and just uses \ for lambda. That's really the only reason I chose ,. The choice of characters is not that important, and easily changed. It just has to be short.

Why, oh why, do we need syntax for anonymous functions? lambda is perfectly fine and legible.

Why, oh why, do we need a Hy? Python is perfectly fine and legible ;) "Need" is a relative term. One may as well ask, "Why does Clojure need #(...) when it has the perfectly fine and legible (fn ...)?". For something so "unnecessary", it sure seems to get used a lot. The #(...) syntax is different from fn in that it doesn't need extra () in the body and does not require a parameter list. It's implied by special names for the parameters themselves.

A major use case for this syntax is partial function application with a minimum of ceremony. This is something one uses quite a lot when writing code in the functional style, especially in a language that does not have automatic currying. This is so important that Python added functools.partial, despite already having the perfectly fine and legible lambda that could easily be used for this purpose. See PEP 0309, for exactly this argument.

If it’s too much to type, ... learn to use your text editor.

Yes, and Java's tedious verbosity is made much more tolerable with a good IDE. Any language you have to excuse via editor has serious issues. A good Lisp should need little more than parentheses-balancing and indent support.

I'm surprised by the lack of enthusiasm here. I do understand (and support) the need to guard against cruft in the language, but this can be taken too far in the other direction. After all, Hy is a Lisp, and if some things are missing in the language, the user will add them. With macros, if necessary. (Like how I just added a macro for function literals.)

If a standard is too minimal for too long (e.g. Scheme, Lua), then the community will fragment because everybody has to re-invent the wheel, but they all do it independently and in incompatible ways. Hy avoids a lot of this kind of problem by supporting Python's libraries, just as Clojure does with Java's. But Clojure still has both fn and #(), and a large core (~600 forms vs Python's 68 builtins), because Java is not a Lisp, and can't help with that part. Python is not a Lisp either. Domain-specific macros will be in Hy libraries, or individual applications. That can't be helped. But the general-purpose macros that might otherwise get re-invented a lot belong in Hy proper, to combat fragmentation.


Reply to this email directly or view it on GitHub.

@gilch
Copy link
Member Author

gilch commented Aug 11, 2015

I wouldn’t want to when I have emacs + slime + paredit.

+ Evil 😈. I do appreciate a good editor. But if you find that you're tempted to write a new emacs function to help you with writing repetitive/boilerplate Lisp (like IDEs do for Java), you're better off writing a macro so it's not repetitive instead.

(lambda [,1 ,2 ,3] (+ ,1 ,2 ,3 -1)) repeats the ,1, the ,2, and the ,3.
The (,\ + ,1 ,2 ,3 -1) macro it was expanded from doesn't.

The problem I have with introducing Clojure’s syntax for LAMBDA is that I find it superfluous for the following reasons:

  1. Python users already type “lambda” (or have their editor do it for them)
  2. CL, Scheme, Racket, and innumerable other Lisps use “lambda"

We already have Clojure's syntax for LAMBDA! It's spelled "fn" in Clojure as well as Hy. (Which is superflous given that we have lambda in Hy.) This is not the same as Clojure's function literals #(), which we do not have. Note, the macro expands into an ordinary lambda form, thus it cannot take lambda away from Hy.

  1. Like DEFN and DEFMACRO, LAMBDA introduces a new form. In contrast QUOTE and friends are only interesting to READ and are only moderate interest to a human reader — ergo reader macros such as ' and , in CL or Scheme.

We can put the ,\ inside the parentheses instead of the #% outside of them, if you prefer. This makes it a macro form, instead of a reader macro.

  1. Parsers would have to be written to support structural editing and highlighting of short-hand lambda syntax (hy-mode in Emacs land for example)

Parsers general enough to support macros will support this macro too. Hy does use macros. A lot.

  1. Special syntax requires beginners to become experts and is non-transferable rote knowledge.
  2. In the case of Hy there is no real benefit other than forcing new users to learn more syntax.

Oh, I agree syntax should be kept as simple as possible... but no simpler.

Hy has an entire contrib section dedicated to anaphoric macros, and even has defmacro/g! built-in. ,\, as proposed, is just another anaphoric macro like these. It's not meant to get rid of lambda. It's meant to get rid of repetition, same as any macro.

@agentultra
Copy link
Member

I still don't understand the benefit this would bring to Hy. I know Clojure. I still don't see the appeal or the benefit it brings users. As near as I can tell fn and lambda being aliases for the same thing is redundant and worth cleaning up.

I'd be more in favour for making better reader macros. Then this discussion would be moot. We could just have lambda and those who think syntactic short-hand improves their programs can run off into the woods and be merry.

Sent from my mobile

On Aug 10, 2015, at 8:30 PM, Matthew Egan Odendahl [email protected] wrote:

I wouldn’t want to when I have emacs + slime + paredit.

  • Evil . I do appreciate a good editor. But if you find that you're tempted to write a new emacs function to help you with writing repetitive/boilerplate Lisp (like IDEs do for Java), you're better off writing a macro so it's not repetitive instead.

(lambda [,1 ,2 ,3](+ ,1 ,2 ,3 -1)) repeats the ,1, the ,2, and the ,3.
The (,\ + ,1 ,2 ,3 -1) macro it was expanded from doesn't.

The problem I have with introducing Clojure’s syntax for LAMBDA is that I find it superfluous for the following reasons:

  1. Python users already type “lambda” (or have their editor do it for them)
  2. CL, Scheme, Racket, and innumerable other Lisps use “lambda"

We already have Clojure's syntax for LAMBDA! It's spelled "fn" in Clojure as well as Hy. (Which is superflous given that we have lambda in Hy.) This is not the same as Clojure's function literals #(), which we do not have. Note, the macro expands into an ordinary lambda form, thus it cannot take lambda away from Hy.

  1. Like DEFN and DEFMACRO, LAMBDA introduces a new form. In contrast QUOTE and friends are only interesting to READ and are only moderate interest to a human reader — ergo reader macros such as ' and , in CL or Scheme.

We can put the ,\ inside the parentheses instead of the #% outside of them, if you prefer. This makes it a macro form, instead of a reader macro.

  1. Parsers would have to be written to support structural editing and highlighting of short-hand lambda syntax (hy-mode in Emacs land for example)

Parsers general enough to support macros will support this macro too. Hy does use macros. A lot.

  1. Special syntax requires beginners to become experts and is non-transferable rote knowledge.
  2. In the case of Hy there is no real benefit other than forcing new users to learn more syntax.

Oh, I agree syntax should be kept as simple as possible... but no simpler.

Hy has an entire contrib section dedicated to anaphoric macros, and even has defmacro/g! built-in. ,, as proposed, is just another anaphoric macro like these. It's not meant to get rid of lambda. It's meant to get rid of repetition, same as any macro.


Reply to this email directly or view it on GitHub.

@rwtolbert
Copy link
Member

👍 to just using reader macros as the short-hand.

@gilch
Copy link
Member Author

gilch commented Aug 11, 2015

As near as I can tell fn and lambda being aliases for the same thing is redundant and worth cleaning up.

Agree with this part, though they don't behave exactly the same now. I would be in favor of removing fn altogether, and using lambda for both cases. But what does that have to do with this issue?

I still don't understand the benefit this would bring to Hy. I know Clojure. I still don't see the appeal or the benefit it brings users. ... I'd be more in favour for making better reader macros. Then this discussion would be moot.

I'm not certain we're even talking about the same thing at this point. You seem to be saying that you're not in favor of adding an alias for lambda like, (lambda [x y z] (+ x y z -1) to (λ [x y z] (+ x y z -1)) or to #λ([x y z] (+ x y z -1)).

But this is not what is being proposed here at all.

I'm suggesting an anaphoric macro like the defmacro/g! we already have, that expands into a lambda. I'm not sure of the exact word or prefix to use (part of what I wanted to discuss), but if you're more familiar with defmacro/g!, it would be something like this:
(fn/p! list p!3 p!1 p!2 "spam"), which would expand into
(lambda [p!1 p!2 p!3] (list p!3 p!1 p!2 "spam")).
Here p! stands for "parameter", just like g! stands for "gensym" in defmacro/g!. Except we'd choose something easier to type than p! for the prefix. The suffix indicates what order the parameters appear in the lambda's parameter list once it gets expanded.

I really don't know what defmacro/g! and the like has to do with reader macros, so that doesn't make any sense.

@tuturto
Copy link
Contributor

tuturto commented Aug 11, 2015

Now that I'm thinking of it, if we do indeed go down the "make it a unicode symbol"-path: what do math people use to denote a parameter? My first intuition would be something like λ1, λ2 (think eigenvalues) or μ1, μ2, etc.

I asked around and did some reading, but didn't find anything that would universally be used to denote a generic parameter. Parameters tend to be given descriptive names (be it x, y, z or something else) that are dependent on the given case. λ1, %1 or $1 don't look too bad to me, but I'm open for other ideas.

@agentultra
Copy link
Member

I see what you're saying now.

Perhaps it'd fit nicely in hy.anaphoric

If it took this amount of discussion to get the point across there's no sense confusing new users.

We don't need confusing syntax for lambda which ever way we cut it.

Sent from my mobile

On Aug 10, 2015, at 10:30 PM, Matthew Egan Odendahl [email protected] wrote:

As near as I can tell fn and lambda being aliases for the same thing is redundant and worth cleaning up.

Agree with this part, though they don't behave exactly the same now. I would be in favor of removing fn altogether, and using lambda for both cases. But what does that have to do with this issue?

I still don't understand the benefit this would bring to Hy. I know Clojure. I still don't see the appeal or the benefit it brings users. ... I'd be more in favour for making better reader macros. Then this discussion would be moot.

I'm not certain we're even talking about the same thing at this point. You seem to be saying that you're not in favor of adding an alias for lambda like, (lambda [x y z](+ x y z -1) to (λ [x y z](+ x y z -1)) or to #λ([x y z](+ x y z -1)).

But this is not what is being proposed here at all. I'm suggesting an anaphoric macro like the defmacro/g! we already have, that expands into a lambda. I'm not sure of the exact word or prefix to use (part of what I wanted to discuss), but if you're more familiar with defmacro/g!, it would be something like this:
(fn/p! list p!3 p!1 p!2 "spam"), which would expand into
(lambda [p!1 p!2 p!3](list p!3 p!1 p!2)).
Here p! stands for "parameter", just like g! stands for "gensym" in defmacro/g!. Except we'd choose something easier to type than p! for the prefix. The suffix indicates what order the parameters appear in the lambda's parameter list once it gets expanded.

I really don't know what defmacro/g! and the like has to do with reader macros, so that doesn't make any sense.


Reply to this email directly or view it on GitHub.

@Foxboron
Copy link
Member

It's honestly not confusing. Clojure uses this to a large extend on map/filter/reduce

user=> (map #(+ %1 10) [1 2 3])
(11 12 13)
user=> (map (fn [a] (+ a 10)) [1 2 3])
(11 12 13)

In my experience it reduces the verbosity on smaller operations.

@agentultra
Copy link
Member

It’s not confusing to someone already familiar with it. They’ve already committed it to memory. Perl is very effective for people who’ve internalized its idioms. It’s initial curve is rather wonky.

If we just want to re-implement Clojure on Python there are better ways to do it. It seems to me that this discussion is starting with a foregone conclusion. Why is this useful for Hy?

(ap-map (+ it 10) [1 2 3])
(ap-filter even? [1 2 3 4 5 6 7 8])

What’s the win here for adding this special syntax?

I think we can just put it in the hy.anaphoric package and those who like it can use it.

On Aug 11, 2015, at 9:40 AM, Morten Linderud [email protected] wrote:

It's honestly not confusing. Clojure uses this to a large extend on map/filter/reduce

user=> (map #(+ %1 10) [1 2 3])
(11 12 13)
user=> (map (fn [a](+ a 10)) [1 2 3])
(11 12 13)

In my experience it reduces the verbosity on smaller operations.


Reply to this email directly or view it on GitHub.

@farhaven
Copy link
Contributor

I think we can just put it in the hy.anaphoric package and those who like it can use it.

👍. Having it in core seems to be a bit overkill indeed. We'd still need to decide on a syntax though. I like @gilch's idea with fn/p! (or lambda/p!?) similar to defmacro/g!. That would also solve the "what prefix do we use for parameters" problem.

@gilch
Copy link
Member Author

gilch commented Aug 11, 2015

Why is this useful for Hy?
(ap-map (+ it 10) [1 2 3])
(ap-filter even? [1 2 3 4 5 6 7 8])
What’s the win here for adding this special syntax?

These do indeed fix the verbosity problem in the particular cases of map and filter. And the other ap- functions have their own uses. But when using the functional style, you need to pass higher-order functions as arguments a lot, not just to builtins like map/filter, but to other functions you may define yourself. An anaphoric lambda will fix the verbosity problem in every case.

The biggest win is for partial function application, as I mentioned before. You need this all the freaking time in functional programming. That's why pure functional languages like Haskell automatically curry their arguments. For example, in Haskell, if you define something equivalent to the Python
foo = (lambda a, b: [a, b])
and then call it without enough arguments:
foo(42)
Then rather than getting an error, it just returns a partial application, like
lambda b: [42, b]
which you can then call later with the remaining arguments, perhaps multiple times with different remaining arguments.

It's never going to be that automatic in Python, but this capability is important enough that Python got functools.partial in PEP 0309. To make this anywhere near as usable as auto-currying, the syntax must be as short as possible. Clojure's form is good enough, but fn/p!, while clearer to you, is too much.

But an anaphoric lambda is even more general than this. It's very common in functional programming to create a pipeline of lazy generators by composing functions. For those who use Unix derivatives, this is very similar to the pipelines one would build in a bash one-liner, for example. But for a pipeline to work the output of one function must match the expected input for the next. Sometimes you need an adapter. Say you need to reverse the order of a binary operator? Anaphoric lambda does it: #(- %2 %1); functools.partial can't do it. Maybe you need convert one argument to a string? Anaphoric lambda does it: #%(list (str %1) %&); functools.partial doesn't. It's nearly as general as lambda itself, but much less verbose.

@gilch
Copy link
Member Author

gilch commented Aug 11, 2015

Parameters tend to be given descriptive names (be it x, y, z or something else) that are dependent on the given case.

We don't have to use a prefix, though maybe we should. Our defmacro/g! takes the prefix approach, but the other anaphoric macros just use a special word (it). It would be possible to treat the symbols x,y,z,w as special this way without a prefix. They would always appear in this order in the parameter list, as in mathematics. This is even shorter. It would kind of limit us to four arguments though. Unless you want to pull them from the &rest arg, but then we're getting verbose again and might as well just use a normal lambda.

Actually, this reminds me of something from my linear algebra course. Vectors of up to four dimensions sometimes used x,y,z,w, but when generalizing to any number of dimensions, it was usually written as all x's with a subscript, like xᵢ. We could use x as the prefix and use x1 x2 x3 x4 x5 etc. for positional arguments. We could even use xi for the &rest parameter. The macro name could be something suitably short, like \ or perhaps even xi, which isn't ambiguous with the &rest part because you can't nest anaphoric lambdas anyway.

What do you guys think of that?

@agentultra
Copy link
Member

On Aug 11, 2015, at 4:00 PM, Matthew Egan Odendahl [email protected] wrote:

Why is this useful for Hy?
(ap-map (+ it 10) [1 2 3])
(ap-filter even? [1 2 3 4 5 6 7 8])
What’s the win here for adding this special syntax?

These do indeed fix the verbosity problem in the particular cases of map and filter. And the other ap- functions have their own uses. But when using the functional style, you need to pass higher-order functions as arguments a lot, not just to builtins like map/filter, but to other functions you may define yourself. An anaphoric lambda will fix the verbosity problem in every case.

The biggest win is for partial function application, as I mentioned before. You need this all the freaking time in functional programming. That's why pure functional languages like Haskell automatically curry their arguments. For example, in Haskell, if you define something equivalent to the Python
foo = (lambda a, b: [a, b])
and then call it without enough arguments:
foo(42)
Then rather than getting an error, it just returns a partial application, like
lambda b: [42, b]
which you can then call later with the remaining arguments, perhaps multiple times with different remaining arguments.

It's never going to be that automatic in Python, but this capability is important enough that Python got functools.partial in PEP 0309. To make this anywhere near as usable as auto-currying, the syntax must be as short as possible. Clojure's form is good enough, but fn/p!, while clearer to you, is too much.

But an anaphoric lambda is even more general than this. It's very common in functional programming to create a pipeline of lazy generators by composing functions. For those who use Unix derivatives, this is very similar to the pipelines one would build in a bash one-liner, for example. But for a pipeline to work the output of one function must match the expected input for the next. Sometimes you need an adapter. Say you need to reverse the order of a binary operator? Anaphoric lambda does it: #(- %2 %1); functools.partial can't do it. Maybe you need convert one argument to a string? Anaphoric lambda does it: #%(list (str %1) %&); functools.partial doesn't. It's nearly as general as lambda itself, but much less verbose.

This argument isn’t going anywhere because of that foregone conclusion I had mentioned. I know what all of this stuff is and I get it. It still doesn’t answer why Hy needs it.

Why does this have to go in core and not in a contrib module? I’m not saying it isn’t useful I just don’t see it being useful in core. Python is not a functional programming language and probably never will be. And it’s not terribly hard to write a module with curry, compose, bind, etc as I did for a monadic parser combinator library I wrote to replace our dependency on rply.

@tuturto
Copy link
Contributor

tuturto commented Aug 11, 2015

Actually, this reminds me of something from my linear algebra course. Vectors of up to four dimensions sometimes used x,y,z,w, but when generalizing to any number of dimensions, it was usually written as all x's with a subscript, like xᵢ. We could use x as the prefix and use x1 x2 x3 x4 x5 etc. for positional arguments. We could even use xi for the &rest parameter. The macro name could be something suitably short, like \ or perhaps even xi, which isn't ambiguous with the &rest part because you can't nest anaphoric lambdas anyway.

Oh, neat. x1, x2, xi and so on would work I think. If we wanted to be fancy, could even use x₁, x₂, etc. as aliases.

I think we can just put it in the hy.anaphoric package and those who like it can use it.

Sounds like a plan to me. If it gets super popular and users start complaining about require they have to use, can then think of moving it to core.

@gilch
Copy link
Member Author

gilch commented Aug 11, 2015

Why does this have to go in core and not in a contrib module? I’m not saying it isn’t useful I just don’t see it being useful in core. Python is not a functional programming language and probably never will be.

You don't need a pure functional language to use the functional style. Just like you don't need an object-oriented language to use the object-oriented style. GTK+ is a great example of object-oriented C. Python supports multiple paradigms. It has lazy generators (yield, genexprs, itertools), higher-order functions, and functools. I use functional style quite a lot in Python.

Lisp is not a functional language either, but it is very multi-paradigm, and Clojure in particular tries to support functional style well. Hy inherits quite a lot from Clojure, thus I assumed supporting functional style with Clojure idioms was a design goal for Hy.

No, we don't have to put it in core. Consensus seems to be, put it in anaphoric and not in core, at least for now--answers one of the main questions I asked in the OP, "would you prefer I put it in core or contrib?"

Oh, neat. x1, x2, xi and so on would work I think.

OK, I'll try to put together a pull using the xi syntax in hy.anaphoric.

@agentultra
Copy link
Member

On Aug 11, 2015, at 5:47 PM, Matthew Egan Odendahl [email protected] wrote:

Why does this have to go in core and not in a contrib module? I’m not saying it isn’t useful I just don’t see it being useful in core. Python is not a functional programming language and probably never will be.

You don't need a pure functional language to use the functional style. Just like you don't need an object-oriented language to use the object-oriented style. GTK+ is a great example of object-oriented C. Python supports multiple paradigms. It has lazy generators (yield, genexprs, itertools), higher-order functions, and functools. I use functional style quite a lot in Python.

Lisp is not a functional language either, but it is very multi-paradigm, and Clojure in particular tries to support functional style well. Hy inherits quite a lot from Clojure, thus I assumed supporting functional style with Clojure idioms was a design goal for Hy.

Preaching to the choir, good buddy. I’ve been working on and off on a monadic parser combinator library to replace our dependence on rply. Hopefully get us to Hy in Hy.

You can program in a functional style in Python. Python will probably never curry functions.

No, we don't have to put it in core. Consensus seems to be, put it in anaphoric and not in core, at least for now--answers one of the main questions I asked in the OP, "would you prefer I put it in core or contrib?"

Oh, neat. x1, x2, xi and so on would work I think.

OK, I'll try to put together a pull using the xi syntax in hy.anaphoric.

Sweet


Reply to this email directly or view it on GitHub.

@gilch gilch mentioned this issue Aug 12, 2015
@gilch
Copy link
Member Author

gilch commented Aug 13, 2015

If we wanted to be fancy, could even use x₁, x₂, etc. as aliases.

I believe it's possible to set up vim/emacs to render it this way. This is probably a cleaner solution than supporting it directly as aliases in Hy, which is also possible, but would complicate the macro a bit.

We could even use xᵢ. The "Latin Subscript Small Letter I " is at U+1D62, which is in Phonetic Extensions, and uncommonly used plane. If your font doesn't have it, you could use "U+00A1: Inverted Exclamation Mark ¡" as a substitute (). I would expect almost all the Unicode fonts to have this. (And if you want to be punny, just call xᵢ ξ).

The digit subscripts ₀₁₂₃₄₅₆₇₈₉ are at U+2080 through U+2089.

@gilch
Copy link
Member Author

gilch commented Aug 13, 2015

xi-forms are merged and working. Try them out.

Was anaphoric the appropriate location? Do we need the Unicode aliases in Hy? Did you notice any bugs? Is the documentation on xi confusing?

If nobody speaks up about further changes in a few days, then I'm going to close this issue.

@farhaven
Copy link
Contributor

I believe it's possible to set up vim/emacs to render it this way.

Yep, I pushed a change that does this to vim-hy yesterday. @algernon has a similar setup for emacs, so no need to do this in Hy proper.

@gilch gilch closed this as completed Aug 17, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants