• Zur Hauptnavigation springen
  • Zum Inhalt springen
  • Zur Seitenspalte springen
  • Zur Fußzeile springen

THE SELF-SERVICE-BI BLOG

Wir lieben Microsoft Power BI

  • Videokurse
    • Coming soon!
  • Live Online Power BI-Schulungen
  • Beratung
    • Was ich für Dich tun kann
    • Showcases
  • Kunden
  • BLOG
    • Business Topics
    • Technical Topics (english)
    • Tools
  • Podcast
  • Kontakt
  • Über

Technical Topics (english) / 19. November 2017

The Environment concept in M for Power Query and Power BI Desktop, Part 3

In our last post Imke and I discussed environments of nested records and let-expressions. The current post covers one native M function, which can define its own environment: Expression.Evaluate(). To explain this function and its environment in detail, we also have to take a look at one ancillary topic: the intrinsic variable #shared. Let’s go…

You can download all code samples from here. If you already have my Power Query editor for Notepad++ (or want to build it) you can open the file in Notepad++. Otherwise open it in a simple text editor and paste it into the Advanced Editor of Power Query or Power BI Desktop.

The global environment

In the last two posts (Part1 and Part2) we discussed environments of expressions in records and let expressions. In addition to these very individual environments, there is (almost) always a global environment. Take a look at the following expression and its result:

Using Table.FromRecord() to define a simple table, Power Query, Power BI Desktop
Using Table.FromRecords() to define a simple table

There are two reasons, why the expression Table.FromRecords({[A=1, B=2],[A=3, B=4]}) returns a table with two columns and two rows:

  1. The function Table.FromRecords() (like any other native M function) is part of the global environment.
  2. The expression Table.FromRecords({[A=1, B=2],[A=3, B=4]}) is evaluated with the global environment being part of the expression’s environment.

That might sound a bit strange. But you will see later, when we talk about the function Expression.Evaluate(), what happens, if there is no global environment. Then come back to this line and read it one more time 🙂

What’s part of the global environment?

Knowing, that there is a global environment might make you think what is part of it. If you want to see the components of the global environment, you can use the (so called) intrinsic variable #shared. #shared returns a record containing all names and values of all the queries (tables, constants, records, lists, native M functions, custom functions, etc.) in the current Excel/ Power BI Desktop file.

Using #shared, to see all parts of the global environment, Power Query, Power BI Desktop
Using #shared, to see all parts of the global environment

All these components are part of the global environment. This is the reason why you can reference a Power Query queries‘ result in another Power Query query. You can’t influence #shared in Power BI Desktop/ Power Query by writing code, but you can create new queries, which then automatically belong to #shared. Now, knowing what the global environment is and knowing about #shared, let’s continue with the function Expression.Evaluate(), which can define its own environment.

Expression.Evaluate()

This M function evaluates a text expression and returns the evaluated value. You can find the official documentation of this function here. Before we move on, we have to give big credits to Chris Webb for his excellent post about Expression.Evaluate(), which very much enlightened us 🙂 . Take a look at this very simple example of how to use this function:

A simple use case of Expression.Evaluate(), Power Query, Power BI Desktop
A simple use case of Expression.Evaluate()

Even though „1 + 1“ is a text, the function evaluates the expression and returns the value 2. This can get pretty handy, when you for example have to create the code to be evaluated on the fly.

Expression.Evaluate() and the global environment

Now let us get back to our former expression Table.FromRecords({[A=1, B=2],[A=3, B=4]}) and evaluate this inside Expression.Evaluate():

Trying to use native m functions inside Expression.Evaluate, Power Query, Power BI Desktop
Trying to use native M functions inside Expression.Evaluate()

Instead of returning the expected table, we get the error message, that the function Table.FromRecords() doesn’t exist in the current context. Replace current context by current environment and it gets a bit clearer: The required function doesn’t exist in the current environment. Expression.Evaluate() is not evaluated in the global environment and this is why it doesn’t even know the function Table.FromRecord() exists. Its environment is empty.

How can we resolve that? Let’s take a look at the syntax of Expression.Evaluate():

Expression.Evaluate(expression as text, optional environment as [...]) as any

The function has a second, optional parameter: Environment. This parameter has to be a record. But how to define the environment with a record? There is good news for you. You already know a variable, that contains all the M functions and returns a record: #shared! Using #shared as second parameter resolves the problem:

#shared delivers the global environment to the Expression.Evaluate() function, Power Query, Power BI Desktop
#shared delivers the global environment to the Expression.Evaluate() function

But not all environmental problems come from the global environment.

Expression.Evaluate() and non global environments

The global environment is not the only possible environmental problem, when it comes to Expression.Evaluate(). Take a look at the following script:

Error message: Expression.Evaluate can't access "outer" variables, Power Query, Power BI Desktop
Error message: Expression.Evaluate() can’t access „outer“ variables

The error message is the same (only the error code is slightly different), as in the example before, but this time it’s not about the global environment, but about the environment of the let expression. The expression behind variable Source cannot access the variables MyVariableA and MyVariableB, because the environment within the Expression.Evaluate() function is empty. You might think, that we could resolve this problem the same way we did last time: by adding #shared?! Unfortunately this is no solution. Even if you include #shared, you get the following error message:

Trying to resolve the problem: #shared (this time) is no help, Power Query, Power BI Desktop
Trying to resolve the problem: #shared (this time) is no help

The variables MyVariableA and MyVariablesB are not part of #shared, as this intrinsic variable only includes the results of queries, not single variables within the queries. So, how could a solution look like? Take a look at the following script:

The solution: But how does it work?, Power Query, Power BI Desktop
The solution: But how does it work?

As you can see this script delivers the desired result: 3! But honestly, what does this [MyVariableA = MyVariableA, MyVariableB = MyVariableB] mean? This is something we learned from Chris and the next screenshot will unravel the mystery:

Connecting inner and outer variables, Power Query, Power BI Desktop
Connecting inner and outer variables

The definition in squared brackets pulls the outer variables MyVariableA and MyVariableB into the environment of the Expression.Evaluate() function and creates a connection between those outer variables and the variables from inside the Expression.Evaluate() function. Usually the inner and outer variables have the same name (e. g. MyVariableA = MyVariableA) what made it hard to understand expresions like [MyVariableA = MyVariableA], but now you know what this syntax is used for. Please notice, that the order plays an important role: It has to be [InnerVariable = OuterVariable]. If you try to change the order, you will get an error message.

Expression.Evaluate with combined global and non global environments

You have seen how to define the global environment inside Expression.Evaluate (by adding #shared) and how to add variables from the same query to the Expression.Evaluate function. Now let’s push it a bit further. What if you need both combined? Let’s say, you want to access outer variables and additionally you want to use a native M function, to calculate a sum of a list:

The need to combine outer variables and the global environment in Expression.Evaluate(), Power Query, Power BI Desktop
The need to combine outer variables and the global environment in Expression.Evaluate()

We need to get the list MyListA inside the environment of Expression.Evaluate(). Additionally we need to add the global environment (#shared) to Expression.Evaluate(), because otherwise List.Sum() does not exist. We’ve got to do that in three steps:

  1. Creating a record, that does the connection between inner and outer variables
  2. Combining that record with #shared (the global environment), which is a record itself
  3. Adding that new record as second parameter of Expression.Evaluate()
The way to do it: Expression.Evaluate with correctly defined environment, Power Query, Power BI Desktop
The way to do it: Expression.Evaluate() with correctly defined global and non global environment

As you can see this leads to the desired result: 3! But you can shorten the code, by creating the combined environment record inside Expression.Evaluate():

The shorter way to do it: Expression.Evaluate() with correctly defined global and non global environment, Power Query, Power BI Desktop
The shorter way to do it: Expression.Evaluate() with correctly defined global and non global environment

Switching between Global and non global Environments

Imagine the following scenario: You have a query, that is called MyValue, which contains the value 12.

Query "MyValue" returns the value 12, Power BI Desktop, Power Query
Query „MyValue“ returns the value 12

Now you write another query, which contains a variable (step) with the same name MyValue, that has the value 1.

Query1 contains a variable (step), which is also named MyValue, Power BI Desktop, Power Query
Query1 contains a variable (step), which is also named MyValue

How can you ensure, that the Result of Query1 refers to Query MyValue and not to the step MyValue, or in other words: How can Query1 return the value 12 instead of 1? With environments:

Using Expression.Evaluate() and #shared to controll the usage of Query results or stap names, Power BI Desktop, Power Query
Using Expression.Evaluate() and #shared to controll the usage of Query results or step names

Because the query MyValue is part of the global environment, we can use Expression.Evaluate() and use #shared as environment. That way the query MyValue is evaluated, and not the variable (step) MyValue 🙂

Expression.Evaluate in a calculated column

Let’s say you have the following table and you need to calculate the sum of both columns by using Expression.Evaluate(). This could be the case, when you try to rebuild the example from this blog post from Imke.

Building a sum over two columns, using Expression.Evaluate, Power Query, Power BI Desktop
Building a sum over two columns, using Expression.Evaluate

The upcoming error message tells us, that the identifier is unknown, or in other words, [Column1] and [Column2] are not part of the environment of the Expression.Evaluate() statement. Inside a table, the underscore „_“ represents the current row, when working with line-by-line operations. The error can be fixed, by adding [_=_] to the environment of the Expression.Evaluate() function. This adds the current row of the table, in which this formula is evaluated, to the environment of the statement, which is evaluated inside the Expression.Evaluate() function.

Adding the "current row" to the environment of Expression.Evaluate, Power Query, Power BI Desktop
Adding the „current row“ to the environment of Expression.Evaluate

At the time of writing Expression.Evaluate() is one of two native M functions, which is able to define its own environment. The other one is SqlExpression.ToExpression()…

SqlExpression.ToExpression()

SqlExpression.ToExpression() is no new native M function. It exists for a long time now. Unfortunately this function has an official documentation which only covers the syntax of the function:

SqlExpression.ToExpression(sql as text, environment as record) as text

Honestly we do not know more about this function, but we wanted to name it here, because it can define its own environment. So if you know more about it, feel free to comment below this post 🙂

Quick recap

What did we see so far?

  • There is a global environment, which exists (almost always) in parallel to the individual environments of each expression. This is the reason why native M functions and other Power Query queries can be accessed in expressions
  • #shared is a variable, returning a record with all the components of the global environment in the current Excel/ Power BI Desktop file
  • There exist two native M functions, which can define their own environment: Expression.Evaluate(Expression, optional Environment) and SqlExpression.ToExpression(Sql-Expression, optional Environment)
  • Without the optional second parameter, the environment of Expression.Evaluate() is empty. That’s why, unless you define the environment correctly, this function can neither access any variable from inside the Power Query query nor use a native or custom M function.
  • The second parameter creates the environment for the defined expressions within the first parameter of the function (e. g. #shared, other variables within the Power Query query, etc.). The second parameter follows the pattern [inner variable = outer variable].
  • If you want, that the environment of your Expression.Evaluate() function consists of the global environment ( e. g. to use native m functions) and an outer variable, then you can do that the following way: Use Record.Combine([inner variable = outer variable], #shared) and then use this as second parameter of the Expression.Evaluate() function.
  • If you are using Expression.Evaluate() in a calculated column and want to reference columns of the current table, you need to use [_=_] as environment, to pull the current row inside the environment of the Expression.Evaluate() function.

The next post will discuss custom M functions and their environments. Stay tuned 🙂

Regards from Germany,

Lars & Imke

I write my posts for you, the reader. Please take a minute and rate the following 3 categories and press „submit“, to help me write even better posts. Thank you 🙂

[yasr_visitor_multiset setid=2]

Lars Schreiber

Lars ist Berater, Entwickler und Trainer für Microsoft Power BI. Er ist zertifizierter Power BI-Experte und Microsoft Trainer. Für sein Engagement in der internationalen Community wurde Lars seit 2017 jährlich durch Microsoft der MVP-Award verliehen. Lies hier mehr…

Kategorie: Technical Topics (english) Stichworte: #sections, #shared, environment concept, Excel, Expression.Evaluate, Power BI Desktop, Power Query

Leser-Interaktionen

Kommentare

  1. editableforms meint

    23. August 2022 um 9:48 am

    Pretty good post. I have really enjoyed reading your blog posts. Anyway, I’ll be subscribing to your feed and I hope you post again soon.
    biggest accounting forms

  2. Jon meint

    14. Dezember 2021 um 4:01 pm

    Hi Lars
    Your post is excellent covering so many aspects of the M environments
    Would like to ask you if you have an idea how to setup local (or external environments) that will contain the non-native (custom) M function. For example if the user have a repository of functions in github will be great to be able to turn it as part of the M environment. Hope my question make some sense
    Cheers
    Jon

  3. Curt Hagenlocher meint

    21. November 2017 um 2:53 pm

    Even though this is technically an implementation detail which I think we reserve the right to change in the future, I’ll just leave this here:

    SqlExpression.ToExpression(„select Column1 from Table1 where Column2 = 1“, [Table1=#table(type table [Column1 as text, Column2 as number], {{„X“, 1}, {„Y“, 2}})])

    • Imke Feldmann meint

      21. November 2017 um 4:14 pm

      Thanks Curt – that is pretty awesome 🙂
      One question though:
      If I convert this to a function that just takes the SQL-code as a parameter, I need to add #shared to the innermost environment, whereas that statement evaluates without any problems as a standalone expression. It seems that this will make Table1 part of the outer #shared-environments as well, which is fine – just that I don’t understand why this syntax actually works. So any explanation would be very welcomed!

      (SqlExpression as text) =>
      let
      Source = Expression.Evaluate( SqlExpression.ToExpression( SqlExpression, [Table1=#table(type table [Column1 as text, Column2 as number], {{„X“, 1}, {„Y“, 2}})] & #shared) , #shared) (shared)
      in
      Source

      • Imke Feldmann meint

        21. November 2017 um 4:26 pm

        Sorry, my bad (had another Table1 in my queries) – so no question here.
        But I will leave the „functionized“ version of the SqlExpression.ToExpression here without a constructed table in the environment. So this will work for all tables that exist in your current environment already:

        (SqlExpression as text) =>
        let
        Source = Expression.Evaluate(SqlExpression.ToExpression(SqlExpression, #shared), #shared) (#shared)
        in
        Source

        • Curt Hagenlocher meint

          21. November 2017 um 5:04 pm

          There are effectively two different environments here. Expression.Evaluate needs one that has the functions from the standard library, while the environment consumed both by SqlExpression.ToExpression and by the function it produces needs to have just the tables referenced by the T/SQL-compatible query text. Naturally, a single combined environment (like the one produced by #shared) can fill both roles.

  4. meng zong meint

    20. November 2017 um 1:55 am

    very cool! thx.
    I have a puzzle for all time. can you take a look.
    Create a var named A = „nihao“
    Create a var named X = Expression.Evaluate( „A“ , #shared )
    and the result of X is „nihao“.
    Click 【Apply and Load】and load data into model.
    if you do this in excel power query , it is OK.
    BUT if you do this in Power BI Desktop, there will be a error.( „A“ is not exist in current context )

    As we know, X = Expression.Evaluate( „A“ , #shared ) can be repaired with X = Expression.Evaluate( „A“ , [A=A]), BUT this is not we expected.

    This use case is very important. we can let user type the table name in text and load the table on the fly. So we can not use the format of Expression.Evaluate( „A“ , [A=A]) , A=A means we must known A before we use.

    do you have any solution of this? very thx.

    • Lars Schreiber meint

      20. November 2017 um 7:34 am

      Hello Meng Zong,

      I must confess that your first example cannot work in Excel from my point of view. If both variables are steps in the same query, then #shared cannot be the solution because #shared delivers the Global Environment and no variable from inside the same query. So in the described case you always have to go for the solution Expression.Evaluate („A“,[A=A]). BUT: I would rather solve the problem described by you with a parameterized function., reading the user’s table selection from the UI (propably Excel) and use this as input parameter for the table selection in Excel or Power BI Desktop.

      Cheers,
      Lars

  5. Maxim Zelensky meint

    19. November 2017 um 11:26 pm

    Hey Lars & Imke!
    Excellent post!
    (BTW, Record.Combine can be substituted with just &, for example:
    [_=_] & #shared

    • Lars Schreiber meint

      20. November 2017 um 6:52 am

      Hey Maxim,

      thanks for your positiv feedback. I wasn’t aware, that „&“ combines two records. Thanks for that hint 🙂

      Cheers,
      Lars

      • Curt Hagenlocher meint

        21. November 2017 um 2:49 pm

        The concatenation operator („&“) works on text, lists, records and tables.

Seitenspalte

WEBSITE DURCHSUCHEN

MELDE DICH FÜR DIE POWER BI USER GROUP HAMBURG AN

Trage Deine E-Mailadresse ein, um für kommende Treffen der PUG Hamburg eingeladen zu werden. Zudem erhältst Du Zugriff auf die Materialien der vergangenen Treffen.

Footer

Kontakt

THE SELF-SERVICE-BI BLOG
Mail: lars@ssbi-blog.de

Rechtliches

  • Impressum
  • Datenschutz

Über THE SELF-SERVICE-BI BLOG

Ich bin ein freiberuflicher Power BI-Berater, -Entwickler und -Trainer und wurde von Microsoft mehrfach mit dem MVP Award ausgezeichnet. Ich arbeite mit einem kompetenten Netzwerk aus freiberuflichen und ambitionierten Kollegen zusammen. Erfahre hier mehr.

Social

Folge mir...

Copyright © 2025 · Digital Pro on Genesis Framework