By the way... why didn't we add something likes this already:
# foldl with initial (type giving) parameter
template foldl(sequence, operation: expr, first): expr =
var result {.gensym.}: type(first)
result = first
for i in countup(0, sequence.len - 1):
let
a {.inject.} = result
b {.inject.} = sequence[i]
result = operation
result
let e: seq[int] = @[]
var foldl_res2 = foldl( e, "(" & $a & "+" & $b & ")", "0")
echo foldl_res2
var foldl_res3 = foldl( @[1,2,3,4,5], "(" & $a & "+" & $b & ")", "0")
echo foldl_res3
I was thinking about doing a PR for this (and similar foldr), just wonder why nobody did it so far?
foldl already injects a and b in the current version.
The difference in the OderWat example is that there is a starting parameter as well. In Scala terminology, what we have now is reduce, while it could be worthwhile to have fold as well
I'm all for foldLeft and foldRight; foldl just happend to be the current name.
By the way , the proposal by Peter Mora would introduce more consistent naming, as well as introducing some new collection functions. Are there any plans to implement (parts of) it?
Well foldl/foldr is so well known that it even is in the wiki article about folds.
And the main difference between the original and my foldl is not just the starting parameter but that the starting parameter can "switch" to another type than the elements of the seq. See my example, there the elements in the seq is of type int but the result is of type string. That was my strongest point about adding this version of the folds.
Peter (I feel like coming back to haunt)
I just care about a/any version of foldl() which can transform the type of a/the seq to something else. Just because I needed it and wondered why foldl() is not foldl() as I expected it :)
Using let is cool of course and should be used to improve the original foldl() asap.
I currently don't mind having a template as it is better than having nothing! Theoretical better designs are nice but what about having something to use now(tm) and for real(tm)!
BTW: Is this how you take a break :-P ... NimLazy is pretty cool btw!
OderWat: Well foldl/foldr is so well known that it even is in the wiki article about folds.
First, I don't really want to sidetrack the discussion, and I don't see a problem with the feature itself.
But foldl/foldr are part of a long-standing irritation of mine where names that (kinda sorta) had a raison d'ĂȘtre in some other context are proliferated. Because, no, foldl/foldr are not well-known in general. They are well-known to an "in crowd", but not to programmers in general (especially new programmers); they're part of a tradition in a subset of functional languages that prefers short cryptic names over understandable ones ("car" = "contents of address part of register" and "cdr" = "contents of decrement part of register" are even more problematic examples). Such programming slang creates language subcultures and hinders accessibility. These days, memory is cheap; editors have autocompletion; there's really no reason to unnecessarily save a few letters.
That said, foldl/foldr are already part of the public interface of sequtils and would have to be deprecated, so (as I said) I suspect that this ship has already sailed. But in general it remains a good idea to reflect on naming conventions and choose appropriate names rather than just copying what's going on elsewhere.
Jehan: They are well-known to an "in crowd", but not to programmers in general (especially new programmers); they're part of a tradition in a subset of functional languages that prefers short cryptic names over understandable ones
Come on, name names! Haskell is the code golfing FPL you're thinking about, right? In OCaml we use fold_left, fold_right. And modern Lisps deprecate car/cdr in favor of first/rest I think.
I agree with everything you wrote, with the caveat that sometimes good taste and reasonable convention can give weight to a shortened name. In the case of foldl/foldr I prefer the longer names; single letter abbreviations are too easy to miss, unlike say 'prev' for 'previous' which is IMO fine.
brianrogoff: Come on, name names!
I'm actually thinking not so much about languages, but research papers in computer science (especially those with a mathematical bend). Haskell does happen to be popular with type theorists, granted, but it's hardly the only language that has been infected by research jargon (and if we're talking languages in general, there's C with fun names such as strpbrk()).
My favorite example of where this happened is still unit. The choice of the name for the type makes perfect sense in the context of type theory (as the type that can hold only one value), but the name is all but meaningless in a programming context (because we never actually conceptualize a function as returning that singular value). Yet it has seen wide and largely unreflected adoption.
I'm on a break. It is completely not my mistake that there are so cool forum topics :)
May I have some additional comments? The current proposal in this thread uses sequence.len and sequence[i]. Actually none of these are necessary, I would support items(). I think that we should use for x in sequence:. This also cancels the need to copy the input sequence with a let command. (In the current mapIt implementation where seq1.len() really matters to preallocate the result, there is a compile time check when compiles(seq1.len), and it uses the for it{.inject.} in seq1 loop, not seq1[i].)
Also if you are already there, there is one thing which is strange in the current foldl(sequence, operation: expr) implementation in sequtils.nim. What happens if you call it with an empty sequence? Well, it has no meaning (in your version where you provide the start value, that has a meaning). In debug mode that's an assert error. In release mode you get back some junk data (sequence[0] returns the first element, which is the memory left there). I believe that we should throw a proper exception in both debug and release mode. One could argue that the current behavior mimics how [] works, and that's right. However, I think it would be more straightforward.
About the naming convention. What happens if I want to generate some csv output. In python I would do something like '\n'.join([','.join([data[r][c] for c in range(columns)]) for r in rows]). In Nim we could use foldl twice. But wait, it injects a and b. What happens if I write a in the most inner scope? What happens if I want to refer to the other a in my expression? I believe that the answer is: foldl(data, (q,w) => ...), so foldl accepts a function, and if I want to have a and b injected, then I would use foldlAb. I'm open to any other solutions which solves this issue.
Peter