Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.

Reply
JeffWeir
Advocate IV
Advocate IV

Struggling to understand SomeFunction= (SomeArgumenx) => ( ) => SomeResult

Hi all. I'm struggling to understand what the PowerQuery Formula Language Specification refers to as Closures.

 

Can someone help explain what the empty parentehis does in the spec guideline below?

 

9.5 Closures
A function can return another function as a value. This function can in turn depend on one or more parameters to the original function. In the following example, the function associated with the field MyFunction returns a function that returns the parameter specified to it:

[
    MyFunction = (x) => () => x, 
    MyFunction1 = MyFunction(1),
    MyFunction2 = MyFunction(2), 
    Result = MyFunction1() + MyFunction2() // 3 
]

 
Each time the function is invoked, a new function value will be returned that maintains the value of the parameter so that when it is invoked, the parameter value will be returned.

 

I've also seen something similar at Ben Gribaudo's blog:

 

let
  Source = { 1, 2, 3, 4, 5 },
  CalculatorGenerator = (discountPercentage) =>
    (value) => (1 - discountPercentage) * value,
  HalfOff = CalculatorGenerator(0.5),
  Result = List.Transform(Source, HalfOff)
in
  Result

 I just can't wrap my head around what is happening here.  Can anyone walk me though the execution of this?

For instance, working backwards from the Result step:

  •   The List.Transform function says “Go get the items from Source, and put them through the HalfOff function".
  • The HalfOff function says “pass these values to the CalculaterGenerator function, and tell it to use the setting of 0.5”
  • And the CaclucatorGenerator function says “Hey, I need a parameter. 0.5, you say?"
  • And the *parameter itself* says "I need a parameter?  How does that work? How does (value) know to use the items in source?
1 ACCEPTED SOLUTION

The isse was that I just hadn’t understood fully about passing “function values” around. (Would ‘Function Step’ or ‘Function Transform’ be a better name?)

 

I come from the world of self-taught VBA in Excel, and so hadn’t heard of Closures or even First Class Functions. I watched a quite good video at https://youtu.be/kr0mpwqttM0 that explained this concept using Python and JavaScript (neither of which I have any experience in), and it kinda sunk in when he assigned a function to a variable, and made the point that he wasn’t assigning the *result* of a function to a variable. And then after that, he could treat that variable as if it were a function, meaning he could pass some argument into it just by going VariableName(SomeValue). And only at that point was anything being executed.

 

I also think the syntax sugar in the List.Transform was confusing me.

View solution in original post

4 REPLIES 4
ImkeF
Super User
Super User

Hi Jeff,

these are function chains. The first function passes an argument to the second function.

 

In your first case, the second function doesn't have an argument, so at the end you're just dealing with one parameter at the end. One might think that this is senseless, but a syntax like this could become useful when you want to write dynamic expressions where in some cases, the second function should contain some values (I'm using a comparable brainless "identity"-function here for example (in fnAllAny) : https://www.thebiccountant.com/2019/05/14/the-full-table-containsanywhere-function-power-query-power...
 )

The second example is a chain where every function gets one parameter. Thats totally equivalent to one function with 2 parameters like so: 

let
  Source = { 1, 2, 3, 4, 5 },
  CalculatorGenerator = (discountPercentage, value) => (1 - discountPercentage) * value,
  Result = List.Transform(Source, each CalculatorGenerator(0.5, _))
in
  Result

 

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

Thanks @ImkeF 

 

What I still don't really understand from the below is how the items of the source list are being assigned to "value".

let
  Source = { 1, 2, 3, 4, 5 },
  CalculatorGenerator = (discountPercentage) =>
    (value) => (1 - discountPercentage) * value,
  HalfOff = CalculatorGenerator(0.5),
  Result = List.Transform(Source, HalfOff)
in
  Result

 

The values in the Source list are being fed one at a time to the HalfOff step.

The HalfOff step calls the CalculaterGenerator function, and feeds to it it's lone parameter input (0.5).

The CaclulatorGenerator step takes that 0.5 argument, but then gets a sub-function to multilpy that discountPercentange against the (value) input. But how did it know what (value) was? Didn't we only feed it's parent (0.5)?

 

I understand that scopoe extends to the Result step, but I don't see an actual assignment anywhere for (value).

 

Whereas in your reworked example, value is explicitly defined: 

CalculatorGenerator = (discountPercentage, value) => (1 - discountPercentage) * value,

 ...and when it is called, there is an explicit assignment of values to it:

each CalculatorGenerator(0.5, _)

 

 

 

 

Oh, I see. Yes, that's indeed special:

 

List.Transform is a function with 2 parameters: 

 

1) The list to iterate through

2) A function with one parameter. That parameter will automatically be filled with the current list item. So you have no control over what parameter goes into that function. (As you just type a function name or declaration and NOT a function call). I'm not so familiar with other programming languages and don't know if there are comparable concepts as well.

 

And there is syntax sugar involved:

The fully declared syntax for: List.Transform(Source, HalfOff)

would be:

List.Transform(Source, each HalfOff(_) )

or

List.Transform(Source, (x) => HalfOff(x) )

 

But due to the syntax sugar, you can simply give the name of the function and M will resolve it automatically (passing in the "non-optional" current list item automatically)

 

So reverting back to the example: If you know that you want to use a function that contains a parameter with (another) function, where the parameter will be passed into automatically (List.Transform) and have actually a function with 2 parameters (Calulate.Generator), you have to split it up into 2 functions, if you want to use the automatic passing of the function parameter (syntax sugar) in List.Transform.

 

Does that make sense?

Imke Feldmann (The BIccountant)

If you liked my solution, please give it a thumbs up. And if I did answer your question, please mark this post as a solution. Thanks!

How to integrate M-code into your solution -- How to get your questions answered quickly -- How to provide sample data -- Check out more PBI- learning resources here -- Performance Tipps for M-queries

The isse was that I just hadn’t understood fully about passing “function values” around. (Would ‘Function Step’ or ‘Function Transform’ be a better name?)

 

I come from the world of self-taught VBA in Excel, and so hadn’t heard of Closures or even First Class Functions. I watched a quite good video at https://youtu.be/kr0mpwqttM0 that explained this concept using Python and JavaScript (neither of which I have any experience in), and it kinda sunk in when he assigned a function to a variable, and made the point that he wasn’t assigning the *result* of a function to a variable. And then after that, he could treat that variable as if it were a function, meaning he could pass some argument into it just by going VariableName(SomeValue). And only at that point was anything being executed.

 

I also think the syntax sugar in the List.Transform was confusing me.

Helpful resources

Announcements
Microsoft Fabric Learn Together

Microsoft Fabric Learn Together

Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City

PBI_APRIL_CAROUSEL1

Power BI Monthly Update - April 2024

Check out the April 2024 Power BI update to learn about new features.

April Fabric Community Update

Fabric Community Update - April 2024

Find out what's new and trending in the Fabric Community.

Top Solution Authors