This would simplify things and let us get rid of def.
Also, I think it would be easier to teach lambdas by having the two major function-creating tasks taken care of by one function, since the anonymous case is a simple removal of the variable-to-be-assigned. Though probably not the highest priority, things that are simpler to beginners are likely to be simpler to the rest of us, too.
There would be a conflict with functions of the form:
(fn args ...)
Because we couldn't tell if it is a vararg lambda or a function named 'args'. I think an acceptable solution would be to make the vararg form be:
(fn ('() . args) ...)
Or something like that. In other words, we would require that the parameters always be in parens. It might not be the prettiest, but vararg isn't very common, and I think the simplification that the alternate form for fn would bring would be worth it.
Firstly, if you want to give a name to a 'fn form, use 'rfn.
Secondly, 'def is not redundant with 'fn because 'def is not an axiom. 'def (defined in arc.arc) is just a convenience macro on top of 'fn (in ac.scm). So if you made 'fn take on the behavior of 'def, you would actually be pulling functionality into the axioms rather than leaving it in the core language.
Thirdly, exactly what would your named form of 'fn do? Would it assign the name in (fn name (...) ....) to the function? That behavior seems very out of line with what 'fn does; in fact, that is specifically what 'def is for. If it does not assign the name, than that is a lot of boilerplate you would be requiring users to type.
Fourthly, the (fn args ...) case is actually fairly common, especially in the core language. If your version of 'fn can't support this case, then it almost certainly can't be used without modifying the core language, not to mention all the other code that has been written so far.
(fn name (...) ...) would be equivalent to the current (def name (...) ...), while (fn (...) ...) would be equivalent to the current (fn (...) ...). the intention isn't to replace the current form of fn, but to give it an optional parameter at the front so that both forms work
btw you can already use def in place of fn:
arc>((def fun (x) (+ x 1)) 3)
4
arc>fun
#<procedure: fun>
this version would be able to handle varargs, just not with the same notation as it currently uses. i mentioned (fn name ('() . args) ...) as a possibility, but i think something better would be
(fn name ((v args)) ...)
sort of like the o notation for optional parameters. (fn name (() . args) ...) isn't that bad either. varargs of the form:
(fn name (a1 . rest) ...)
would be unchanged. the only requirement necessary is that args always be in parens, and the only place where that currently conflicts is with full varargs
Even if your intention is to just add an optional parameter to 'fn, you still have to implement it in ac.scm. You can't even redefine it in Arc because it is dealt with specially in 'ac.
I still think there is a logical distinction between defining a function literal, and assigning that function to a variable. One should be implemented as an axiom, and the other really should be implemented in the core language. Pulling the latter into the former just doesn't make sense.
As for varargs notations, the only one I like is (fn ((v args)) ...). Because (fn args ...) is basically a case of destructuring, the (fn (() . args) just doesn't make sense. In fact that notation actually does work in the current Arc, it just throws away the first argument. (Which makes sense if you think through what that code is actually saying.)
But even considering the possible alternatives, I still think the current syntax is the most elegant.
how about if i said: "make the name parameter of def be optional and rename it fn, and rename the current fn to axiomfn" ? we wouldn't be touching the axioms in that case except with a rename
i'm just saying there should be a functionality like that. how the internals are structured to meet that end isn't as significant so long as we're not butchering things. and i'm a big fan of the concise axiomatic approach. it's the reason i use Lua instead of Ruby. and that's why i think merging def and fn would be better -- because to the programmer it would be simpler, even though underneath it may need a bit more sophistication to handle the special case of full varargs
There's still this logical distinction between creating an anonymous function and assigning it to a variable, and I don't think merging 'def and 'fn captures this distinction.
Note the differences in the use of 'def and 'fn. 'fn is used most frequently as a local function, while 'def is a global definition. If you provide a named form of 'fn that assigns a global name, then it will be really tempting to misuse that form in a local context where you really shouldn't be messing with global bindings.
And why should 'def get a pulled into 'fn but not 'afn or 'rfn? In some ways, those make much more sense than the integrated 'def form, but note that all of those were made explicitly different in the core language. We want to make it explicit how we are handling the naming (and scoping) of our functions. Thus the four or more different ways to define a function.
>
> There's still this logical distinction between creating an anonymous function and assigning it to a variable, and I don't think merging 'def and 'fn captures this distinction.
(fn name (arg) ...) and (fn (arg) ...) seem pretty distinct to me
>
>Note the differences in the use of 'def and 'fn. 'fn is used most frequently as a local function, while 'def is a global definition. If you provide a named form of 'fn that assigns a global name, then it will be really tempting to misuse that form in a local context where you really shouldn't be messing with global bindings.
if fn is being used in a local context they probably won't need to give it a name
for afn and rfn i was actually thinking they could be merged as afn:
then there would only be two function-creating thingies: fn and afn. i stated elsewhere that i see the necessity for 'and' and 'aand'. fn and afn are substantially different in that the variable environments of their evaluated bodies are different, as well as producing different classes of functions
truthfully it would be nice if they could all be merged into one, and they could be, except for example 'self' would always be pre-set in lambdas which would be (conceptually) annoying. that would be tolerable though
> > There's still this logical distinction between creating an anonymous function and assigning it to a variable, and I don't think merging 'def and 'fn captures this distinction.
> (fn name (arg) ...) and (fn (arg) ...) seem pretty distinct to me
Think about the difference between (car x) and (scar x). Those are two distinct cases (assignment and retrieval), we wouldn't want to create a single 'car function that would do assignment if given an optional parameter.
(_car x) => (car x)
(_car x y) => (scar x y)
Because these are two entirely different concepts. Similarly the assignment of a function to a variable is distinct from creating that function. In my opinion, merely creating an optional parameter for it in 'fn doesn't capture that distinction.
> if fn is being used in a local context they probably won't need to give it a name
Then why do 'afn and 'rfn exist?
> fn and afn are substantially different in that the variable environments of their evaluated bodies are different
And thus 'fn and 'def are different in that 'fn creates no binding and 'def creates a global binding.
fn and def both create functions. in one case we donate money. in the other we donate money and get a star with our name on it. in both cases we've donated money. car vs scar would be like donating money vs robbing a bank
>> if fn is being used in a local context they probably won't need to give it a name
> Then why do 'afn and 'rfn exist?
to create self-referencing functions
> And thus 'fn and 'def are different in that 'fn creates no binding and 'def creates a global binding.
yes, and afn can create a different class of functions from fn. almkglor had a good point about requiring gymnastics in some cases with a God Fn. those sorts of issues wouldn't happen with (fn,def) -> fn and (afn,rfn) -> afn, except for the full vararg thing
if we use software structure/logic as the only metric, sure, they're all very different, but then we'll lose sight of the way the end user sees all these things
> fn and def both create functions. in one case we donate money. in the other we donate money and get a star with our name on it.
No, the star gets permanently placed in the floor of the quad outside the central office. As opposed to 'afn and 'rfn where we get to keep the star.
Admittedly my 'car and 'scar example wasn't accurate. Let me try again. Say we have a version of 'table called 'ntable that is just like table except it assigns the new table to a global variable. Now suppose we redefine 'table to take an optional parameter, in which case it acts like 'ntable. But all this does is confuse the purpose of 'table, which is to create a table, and 'ntable which creates a table in the global namespace. The overloading here doesn't help shorten or clarify code. So we remove one function from the global namespace. So what?
Unlike in real life, the presence or absence of the star really does matter here, enough that the two shouldn't be condensed into a single operation.
>>> if fn is being used in a local context they probably won't need to give it a name
>> Then why do 'afn and 'rfn exist?
> to create self-referencing functions
Exactly! So you can use the function in more places than just the return value. 'def is similar in that the name allows it to be referenced elsewhere (or inside the definition), and this is totally different from the one-shot usage that 'fn gets most of the time.
> if we use software structure/logic as the only metric, sure, they're all very different, but then we'll lose sight of the way the end user sees all these things
I think we are making very different assumptions about the way the user thinks about 'fn and 'def. When I see 'fn, I think local/anonymous function. When I see 'def on the other hand, I think global/named function (and yes, where the name goes is an important issue, even to an end user). I don't think it is a simplification of this model to merge the two operations.
when i see fn, i see "fundamental function-creating function." i see def as "(= name (fundamental function-creating function))." i said elsewhere that i can see how someone familiar with lisp would see fn and def as essentially lambda and set, and
(lambda name (arg) ...)
would definitely be odd. but the name isn't "lambda," it's "fn." and i conceive that as "function," which has no semantic bias to locality or scope
So no. I don't think this puritan attempt at merging ideas is a good one.
"All processes are impermanent ... All processes are afflicted ... All phenomena are not ‘Self’; when this is seen with knowledge, one is freed from the illusion of affliction. This is the pathway to purity."
i wonder how we would refer to the different selves within the inner function without explicitly setting something in the outer one or using rfn. a let around the inner function isn't that bad. in any case, good catch
I don't like it. If you want to get rid of def, use =. If you want to teach anonymous functions, just say : "you see, (def foo (x)) is the same as assigning the variable foo a function that takes one parameter named x. This can be written : (= foo (fn (x))".
And it makes fn have a strange behavior, plus the fact that we lose the simple syntax for varargs functions. And how do you read (fn nil nil) ? Is it a function I try to call nil with no argument and no body ? Is it a no argument anonymous function that returns nil ?
with the requirement that the parameters always be in parens, that would be an error
[edit]the inspiration for giving fn an optional parameter is that def is redundant. i don't think it would be strange at all. except for full-vararg cases, a straightforward 'def -> fn' search & replace would be sufficient
"with the requirement that the parameters always be in parens, that would be an error" : not really, because without parameters, you would have '() which is automatically translated to nil. You could do (fn (nil) nil), however, but I don't like the look of it. Not enough consistent, I guess (and a list containing the empty list to denote the empty list does not look right to me).
well, as a special form i don't think it would have trouble differentiating between () and nil. () seems to be different from '() even though (is () '()) is t
'() is (quote ()) while () is (). You won't notice the difference if you pass both to a normal function but you might if you passed both to a macro or special form.
While superficially (fn (. args) ...) may look decent, it is entirely inconsistent with the Arc reader. Function definitions have to be entirely representable in lists, and the example you list above is not a valid list. Not only would this break the current Arc, but you would have to reimplement the entire reader that Arc currently borrows from Scheme. Not to mention that you would have to define what it even means to have a cons with no 'car field.
Simplification!? You seem confused. We have two concepts here. Using one symbol for two concepts does not combine two concepts into one. All it does is add confusion. Overloading is fine for some things (eg. + adds numbers and joins lists), but be careful with it. Remember that post (which I'm too lazy to track down right now) titled "You want brevity? You can't handle brevity!" that gave an example of K code? That language wanted to fit APL into ASCII, and did so by overloading the punctuation characters over 4 times each on average. That results in code like this:
(!R)@&{&/x!/:2_!x}'!R
Again, overloading should be used sparingly, and only when the concepts are related in a certain way that I won't bother trying to articulate right now.
I think that in this case any benefits this might have are outweighed by the confusion it would cause and the kludge required for certain things like varargs.
(fn (() . args) body)
Doesn't that hurt your eyes? Not to mention what it is saying: "Function that accepts the empty list and any number of other arguments". That doesn't make sense to me.
>
> Simplification!? You seem confused. We have two concepts here. Using one symbol for two concepts does not combine two concepts into one.
i don't think these are distinct concepts. to me they are the exact same thing except in one case we're attaching a name to what we're making, and in the other we aren't. i can see how people familiar with lisp might see essentially 'lambda' and 'set' in the forms, but on the surface 'fn' and 'def' are very similar
i see the need for say, 'and' and the anaphoric 'aand', but to me def is so redundant with fn i actually do use that (= name (fn (x) ...)) form someone mentioned here
>
> Again, overloading should be used sparingly, and only when the concepts are related in a certain way that I won't bother trying to articulate right now.
i agree, but again i don't think this is an inappropriate case. what i would like to see in Arc is something that Lua does in various places. the best example is its tables, which are simultaneously arrays, hash tables, and with its metatable system, any other structure you can think of from multiple-inheritance objects to arbitrarily-linked lists. and they act respectively depending on how they're used. eg if you use a table as an array it will iterate fast. the result is that the programmer is relieved of the details of choosing and using structures, and can instead focus on actually using them
is Lua's implementation of tables more sophisticated than arrays, hash tables, etc would have been individually? definitely. but it relieves the programmer of a lot of thought that is able to focus on something else. tables are definitely simpler than if their functionality was spread out, yet no functionality is lost compared to such a model. in fact, functionality is gained through the flexibility
by upping the sophistication underneath, a language can become simpler. if you only rely on the sort of simplicity brought about by a pure axiomatic approach, you get a sort of "assembly" simplicity. is MOV DX,8 simple? yes, but for naught
(not to blitzkrieg with text, but i thought i would explain the philosophy in one place)
>
> for certain things like varargs
full varargs would be the only alteration
>
> Doesn't that hurt your eyes?
yes, which is why i proposed another syntax. someone may be able to come up with something better. i use full varargs commonly so it wouldn't be easy for me to let go of the current syntax either
It is true though that whenever you overload an operator you depend on the programmer understanding more about a single operation. Thus by definition overloading increases the complexity of a language.
There is some significant complexity in setting a variable. Note the current existence of four (or more) different forms of 'fn: 'fn (anonymous), 'afn (capturing 'self as a local name), 'rfn (using a user defined local name), and 'def (using a user defined global name). All these forms exist for the purpose of creating functions, but they each has a different way of setting varaibles.
How do you capture that complexity if you want to start combining them into a single operation? Do you just ignore everything except 'fn and 'def and say the user can type the extra character for 'afn and 'rfn? That seems rather inconsistent.
>
> It is true though that whenever you overload an operator you depend on the programmer understanding more about a single operation. Thus by definition overloading increases the complexity of a language.
that's if we're looking at it from the perspective of the operator. but if we look at it from the perspective of an expression, i think it is simpler. to illustrate: one of the nice examples on the front page of ruby-lang.org is the following (keeping their comments):
# Ruby knows what you
# mean, even if you
# want to do math on
# an entire Array
cities = %w[ London
Oslo
Paris
Amsterdam
Berlin ]
visited = %w[Berlin Oslo]
puts "I still need " +
"to visit the " +
"following cities:",
cities - visited
if we look at it from the perspective of the operators, we see we've overloaded + and - for non-numeric types. however, if we look at it from the perspective of the programmer as they are writing those lines, we see that the programmer just wants to say "remove the cities i've visited from the cities i have to visit," and "cities - visited" is almost a direct translation of that. it makes sense. the fact that that operator happens to be overloaded from another domain is irrelevant. the context secures the role of the operator
There is no syntax for varargs. Destructuring argument lists are very simple and easy to understand once you understand the structure of Lisp code. What you're proposing is taking away the ability to manipulate s-expressions as they are just to free up another name that would rarely be used anyway.
Why are you complaining about def being redundant, instead of railing against let? Nobody will argue that let isn't redundant (although many will say that it's useful).
i see 'let' as the core operator, and 'with' as the one that is redundant. you're right though. i wonder if they couldn't be combined into just let. i don't see the difficulty in something like
but the intention isn't to merge everything for the sake of it, it's to simplify the surface "interface" for the programmer. fn/def and let/with are both things the programmer will use often
though in the case of let/with it may be more readable to keep the current setup since it saves the eyes the trouble of checking how many variables are bound, even though it's just a check for parens. i haven't programmed enough to really say
I agree with you that 'let and 'with are redundant (although I still disagree about 'fn and 'def).
But I think the difference between the 'let and 'with forms would be removed better simply by removing the implicit progn. This was brought up previously in http://arclanguage.org/item?id=3234 .
This would allow (let a 1 form) like the current 'let, or (let a 1 b 2 form) like the current 'with. For multiple statements you would need (let a 1 (do forms)), but pg already said how he liked that 'do highlighted non-functional code.