You are viewing an old revision of this post, from February 27, 2011 @ 20:39:26. See below for differences between this version and the current revision.

At work recently, I was writing some code which wanted to add all the elements of a collection of strings to a document writer. This seemed like a classic case for foreach, so I wrote something like this:

data.foreach(s => writer.addDocument(createDocument s))

(Warning: I’m typing this from memory, without trying it out in the Scala CLI, and I’m new enough to Scala that it’s entirely possible that I’m making silly syntax errors.) That was great, but Scala lets you use underscores in place of explicit argument names for arguments that you’re only going to use once, so I tried doing this instead:

data.foreach(writer.addDocument(createDocument _))

That, however, didn’t work. After looking at the error message and talking to some coworkers, it seemed like Scala was parsing it as the following:

data.foreach(writer.addDocument(s => createDocument s))

And there isn’t a version of addDocument that takes a function argument. Which is good: if there had been, it might have compiled but not done what I wanted, which would have been even more confusing! Still, I was frustrated: why can’t Scala just read my mind? But, honestly, the compiler’s choice was a perfectly reasonable way to parse that expression, and I certainly wouldn’t want Scala’s parsing to be dependent on the types that function calls accept. So I was willing to leave it at that.

Except that, as one of my coworkers then pointed out, there was another way of breaking it down: instead of writing as a single foreach call, I would write it as map plus foreach, as follows:

data.map(createDocument).foreach(writer.addDocument _)

Which is much nicer! So, actually, Scala’s parse error was gently nudging me in the correct direction: yes, this sort of thing is potentially ambiguous to parse, but if you break down your function composition properly, then that ambiguity goes away. So it was nice of the compiler to help me write my code elegantly!

The funny thing was, I ran into a very similar situation an hour later: it involved two maps plus a foreach, but the exact same principle applied. That time, though, after writing it out as a chain of three collection functions, I didn’t really like the result: it was going too far into the details of how to use a library that I was integrating with, and I didn’t find the result particularly evocative. So I ended up going back to the single-foreach version, but this time I pulled out the function that I was applying to a member function, so I could give it a name that explained what was going on. It’s nice to have different tools in your toolkit, because ultimately you need to be guided by what makes your code the most expressive rather than falling in love with the most powerful tool.

Post Revisions:

Changes:

February 27, 2011 @ 20:39:26Current Revision
Content
Unchanged: At work recently, I was writing some code which wanted to add all the elements of a collection of strings to a document writer. This seemed like a classic case for <code>foreach</code>, so I wrote something like this:Unchanged: At work recently, I was writing some code which wanted to add all the elements of a collection of strings to a document writer. This seemed like a classic case for <code>foreach</code>, so I wrote something like this:
Unchanged: <code>data.foreach(s => writer.addDocument( createDocument s))</code>Unchanged: <code>data.foreach(s => writer.addDocument( createDocument s))</code>
Unchanged: (Warning: I'm typing this from memory, without trying it out in the Scala CLI, and I'm new enough to Scala that it's entirely possible that I'm making silly syntax errors.) That was great, but Scala lets you use underscores in place of explicit argument names for arguments that you're only going to use once, so I tried doing this instead:Unchanged: (Warning: I'm typing this from memory, without trying it out in the Scala CLI, and I'm new enough to Scala that it's entirely possible that I'm making silly syntax errors.) That was great, but Scala lets you use underscores in place of explicit argument names for arguments that you're only going to use once, so I tried doing this instead:
Unchanged: <code>data.foreach( writer.addDocument( createDocument _))</code>Unchanged: <code>data.foreach( writer.addDocument( createDocument _))</code>
Unchanged: That, however, didn't work. After looking at the error message and talking to some coworkers, it seemed like Scala was parsing it as the following:Unchanged: That, however, didn't work. After looking at the error message and talking to some coworkers, it seemed like Scala was parsing it as the following:
Unchanged: <code>data.foreach( writer.addDocument(s => createDocument s))</code>Unchanged: <code>data.foreach( writer.addDocument(s => createDocument s))</code>
Unchanged: And there isn't a version of <code>addDocument</code> that takes a function argument. Which is good: if there had been, it might have compiled but not done what I wanted, which would have been even more confusing! Still, I was frustrated: why can't Scala just read my mind? But, honestly, the compiler's choice was a perfectly reasonable way to parse that expression, and I certainly wouldn't want Scala's parsing to be dependent on the types that function calls accept. So I was willing to leave it at that.Unchanged: And there isn't a version of <code>addDocument</code> that takes a function argument. Which is good: if there had been, it might have compiled but not done what I wanted, which would have been even more confusing! Still, I was frustrated: why can't Scala just read my mind? But, honestly, the compiler's choice was a perfectly reasonable way to parse that expression, and I certainly wouldn't want Scala's parsing to be dependent on the types that function calls accept. So I was willing to leave it at that.
Unchanged: Except that, as one of my coworkers then pointed out, there was another way of breaking it down: instead of writing as a single <code>foreach</code> call, I would write it as <code>map</code> plus <code>foreach</code>, as follows:Unchanged: Except that, as one of my coworkers then pointed out, there was another way of breaking it down: instead of writing as a single <code>foreach</code> call, I would write it as <code>map</code> plus <code>foreach</code>, as follows:
Deleted: <code>data.map( createDocument) .foreach(writer.addDocument _)</code> Added: <code>data.map( createDocument) .foreach(writer.addDocument)</code>
Unchanged: Which is much nicer! So, actually, Scala's parse error was gently nudging me in the correct direction: yes, this sort of thing is potentially ambiguous to parse, but if you break down your function composition properly, then that ambiguity goes away. So it was nice of the compiler to help me write my code elegantly!Unchanged: Which is much nicer! So, actually, Scala's parse error was gently nudging me in the correct direction: yes, this sort of thing is potentially ambiguous to parse, but if you break down your function composition properly, then that ambiguity goes away. So it was nice of the compiler to help me write my code elegantly!
Unchanged: The funny thing was, I ran into a very similar situation an hour later: it involved two <code>map</code>s plus a <code>foreach</code>, but the exact same principle applied. That time, though, after writing it out as a chain of three collection functions, I didn't really like the result: it was going too far into the details of how to use a library that I was integrating with, and I didn't find the result particularly evocative. So I ended up going back to the single-<code> foreach</code> version, but this time I pulled out the function that I was applying to a member function, so I could give it a name that explained what was going on. It's nice to have different tools in your toolkit, because ultimately you need to be guided by what makes your code the most expressive rather than falling in love with the most powerful tool.Unchanged: The funny thing was, I ran into a very similar situation an hour later: it involved two <code>map</code>s plus a <code>foreach</code>, but the exact same principle applied. That time, though, after writing it out as a chain of three collection functions, I didn't really like the result: it was going too far into the details of how to use a library that I was integrating with, and I didn't find the result particularly evocative. So I ended up going back to the single-<code> foreach</code> version, but this time I pulled out the function that I was applying to a member function, so I could give it a name that explained what was going on. It's nice to have different tools in your toolkit, because ultimately you need to be guided by what makes your code the most expressive rather than falling in love with the most powerful tool.

Note: Spaces may be added to comparison text to allow better line wrapping.