Scala柯里化foldLeft方法的类型参数及语法相关技术问询
foldLeft Method Signature Step by Step Great question—foldLeft is such a fundamental method in Scala, but its signature can look intimidating at first, especially when you're getting used to generics and currying. Let's unpack each part one by one:
1. What's the [B] in square brackets, and why is it there?
That [B] is a generic type parameter. Square brackets are Scala's syntax for declaring placeholder types that the method can work with, without tying it to one specific type.
Think of B as a "blank" we fill in later: when you call foldLeft, the compiler will figure out what B actually is based on how you use the method. For example, if you're folding a list of integers to calculate their sum, B will be Int; if you're folding the same list to build a string of all numbers, B will be String. Generic parameters like B make foldLeft reusable across tons of different use cases instead of needing a separate method for every possible result type.
Also, note that A here (from the (B, A) => B part) is the type of elements in the original collection (like the Int in List[Int]). Most collection methods in Scala already have A as a generic parameter defined at the class level (e.g., List[A]), so foldLeft just adds B for the result type.
2. Is the B after the colon the return type? What does it mean?
Yep! That trailing B is absolutely the return type of foldLeft, and it refers to the same B we declared in the square brackets. So the entire method returns a value of whatever type B ends up being.
Using our earlier examples: when summing integers, the return type is Int (matching B), and when building a string, the return type is String (also matching B). It's the final result you get after folding through the entire collection.
3. The two parameter lists, and the (B, A) => B function type
You're right—foldLeft uses curried parameter lists (splitting parameters into multiple parentheses groups) instead of one big list. There are two main reasons for this:
- It helps with type inference: once you pass the first parameter
z(which is of typeB), the compiler can automatically figure out whatBis, so you don't have to explicitly specify it when writing theopfunction. - It makes partial application easier: you can fix the first parameter
zand get a reusable function that only needs theopparameter (e.g.,val sumFold = numbers.foldLeft(0)_gives you a function that takes an(Int, Int) => Intand returns the sum).
Now for the op: (B, A) => B part: this is a function type, where:
- The left
(B, A)are the function's two input types:- First input:
Bis the current accumulated value (starts asz, gets updated each step) - Second input:
Ais the current element from the collection we're folding over
- First input:
- The right
Bis the function's return type: this is the new accumulated value after combining the current accumulator with the collection element. It has to beBto keep the folding process consistent—you can't suddenly switch from accumulating integers to strings mid-fold!
Quick Example to Tie It All Together
Let's make this concrete with code:
val fruits = List("apple", "banana", "cherry") // Fold to count total characters: B is Int, z is 0, op adds current fruit's length to accumulator val totalChars = fruits.foldLeft(0)((acc, fruit) => acc + fruit.length) // totalChars is 15 (5 + 6 + 4) // Fold to build a comma-separated string: B is String, z is "", op appends fruit with a comma val fruitString = fruits.foldLeft("")((acc, fruit) => if (acc.isEmpty) fruit else s"$acc, $fruit") // fruitString is "apple, banana, cherry"
内容的提问来源于stack exchange,提问作者Jwan622

