Funciones Especiales

Las características más avanzadas de M: #shared, Expression.Evaluate, #table, Character.* y patrones expertos.

Intermedio Avanzado
🌐

#shared — El entorno global de M

#shared

Avanzado

Sintaxis

#shared as record

¿Qué hace?

#shared devuelve un record con todas las funciones y valores disponibles en el entorno M actual. Es la forma de hacer introspección del lenguaje: ver qué funciones existen, explorar su documentación interna y hacer lookup dinámico de funciones por nombre.

Ejemplo

// Ver todas las funciones disponibles
let
    todas = #shared,
    // Filtrar sólo funciones de texto
    deTexto = Record.SelectFields(
        todas,
        List.Select(Record.FieldNames(todas), each Text.StartsWith(_, "Text."))
    )
in deTexto

// Obtener documentación de una función
let
    doc = Value.Metadata(#shared[Text.Upper])
in doc   // Record con Summary, Category, etc.

// Llamar función por nombre (dinámico)
let
    nombreFn = "Text.Upper",
    fn       = Record.Field(#shared, nombreFn)
in fn("hola kawaii")   // "HOLA KAWAII"
💡 Tip kawaii: En el Editor Avanzado de Power Query, escribe #shared como única expresión y ejecútalo. Verás una tabla con todas las funciones del lenguaje con su documentación — ¡es como la referencia oficial integrada!
🆕

#table — Crear tablas literales

#table

Intermedio

Sintaxis

#table(columns as list, rows as list) as table
// columns: lista de nombres de columna (o un tipo de tabla)
// rows: lista de listas (cada sublista es una fila)

¿Qué hace?

Crea una tabla directamente desde datos en M, sin necesidad de una fuente de datos externa. Ideal para tablas de parámetros, tablas de referencia, datos de prueba y configuración embebida.

Ejemplo

// Tabla simple
#table(
    {"Mes", "Nombre", "Trimestre"},
    {
        {1, "Enero",   1}, {2, "Febrero",  1}, {3, "Marzo",    1},
        {4, "Abril",   2}, {5, "Mayo",     2}, {6, "Junio",    2},
        {7, "Julio",   3}, {8, "Agosto",   3}, {9, "Septiembre",3},
        {10,"Octubre", 4}, {11,"Noviembre",4}, {12,"Diciembre",4}
    }
)

// Con tipos explícitos (más eficiente)
#table(
    type table [#"Código" = text, #"Valor" = number, #"Activo" = logical],
    {
        {"A01", 100.5, true},
        {"A02", 200.0, false},
        {"A03", 150.75, true}
    }
)

// Tabla de parámetros de configuración
let
    Parametros = #table(
        {"Nombre", "Valor"},
        {
            {"Servidor",    "prodserver"},
            {"BaseDatos",   "ventas_2024"},
            {"Anyo",        "2024"},
            {"MaxRegistros", "50000"}
        }
    ),
    Config = Record.FromTable(Parametros)
in Config[Servidor]  // "prodserver"
📌 Buena práctica: Usa #table con tipos explícitos para tablas que sabes que nunca cambiarán de estructura (tablas de meses, días de la semana, mapas de códigos). Mejora el rendimiento al evitar la inferencia de tipos.
🧪

Expression.Evaluate — Evaluación dinámica

Expression.Evaluate

Avanzado

Sintaxis

Expression.Evaluate(document as text, optional environment as nullable record) as any

¿Qué hace?

Evalúa una expresión M escrita como texto en tiempo de ejecución. El parámetro environment permite pasar un record con los nombres disponibles en la expresión (normalmente #shared para tener todas las funciones).

Ejemplo

// Evaluación básica
Expression.Evaluate("1 + 1")   // 2

// Con funciones del entorno
Expression.Evaluate(
    "Text.Upper(""hola kawaii"")",
    #shared
)
// "HOLA KAWAII"

// Caso de uso real: fórmula almacenada en tabla
let
    tablaFormulas = #table({"Campo", "Formula"}, {
        {"NombreCompleto", """[Nombre] & "" "" & [Apellido]"""},
        {"Importe_IVA",    "[Importe] * 1.21"}
    }),
    // Aplicar fórmula dinámica a cada fila de datos
    formula = tablaFormulas{0}[Formula],
    fnDinamica = Expression.Evaluate(
        "(fila) => " & formula,
        #shared
    )
in Table.AddColumn(datos, "NombreCompleto", fnDinamica)
⚠️ Ojo con esto: Expression.Evaluate puede ejecutar cualquier código M, incluido código malicioso si la expresión viene de una fuente no confiable. Úsalo sólo con expresiones que controles completamente. En Power BI Service, puede estar deshabilitado por políticas de seguridad.

Expression.Constant / Expression.Identifier

Avanzado

Sintaxis

Expression.Constant(value as any) as text
Expression.Identifier(name as text) as text

¿Qué hace?

Expression.Constant serializa un valor M a su representación como texto M (el inverso de Expression.Evaluate). Expression.Identifier genera el texto correcto para un identificador M, incluyendo las comillas necesarias para nombres con espacios.

Ejemplo

Expression.Constant(42)           // "42"
Expression.Constant("hola")       // """hola"""
Expression.Constant(#date(2024,1,1))  // "#date(2024, 1, 1)"
Expression.Constant({1,2,3})      // "{1, 2, 3}"

Expression.Identifier("Nombre")           // "Nombre"
Expression.Identifier("Mi Campo Especial")  // "#""Mi Campo Especial"""
Expression.Identifier("Año Fiscal")        // "#""Año Fiscal"""
🔤

Character.* — Caracteres Unicode

Character.FromNumber / Character.ToNumber

Intermedio

Sintaxis

Character.FromNumber(number as number) as text
Character.ToNumber(character as text) as number

¿Qué hace?

Convierte entre caracteres Unicode y sus códigos de punto de código. Útil para generar caracteres especiales, emojis y procesar textos con caracteres de control.

Ejemplo

Character.FromNumber(65)      // "A"
Character.FromNumber(9)       // "\t" (tabulador)
Character.FromNumber(10)      // salto de línea LF
Character.FromNumber(128512)  // "😀" (emoji cara sonriente)

Character.ToNumber("A")   // 65
Character.ToNumber("ñ")   // 241

// Insertar salto de línea en texto
"Primera línea" & Character.FromNumber(10) & "Segunda línea"

// Alternativa con literales de escape en M
"Primera línea" & "#(lf)" & "Segunda línea"
💡 Tip kawaii: M tiene literales de escape especiales: #(lf) = salto de línea, #(cr) = retorno de carro, #(tab) = tabulador, #(null) = carácter nulo. Son equivalentes a Character.FromNumber con los códigos correspondientes.
⚙️

Información del Entorno

Environment.UserName / Environment.MachineName

Intermedio

Sintaxis

Environment.UserName() as text
Environment.MachineName() as text
Environment.UserDomainName() as text

¿Qué hace?

Devuelve información del entorno de ejecución: nombre de usuario Windows, nombre de máquina y dominio. Útil para logs, auditoría y personalización de conexiones por usuario.

Ejemplo

// Log de auditoría
Table.AddColumn(tabla, "CargadoPor",
    each Environment.UserName() & " en " &
         DateTime.ToText(DateTime.LocalNow(), "yyyy-MM-dd HH:mm"))

// Ruta personalizada por usuario
let
    usuario = Environment.UserName(),
    ruta    = "C:\Users\" & usuario & "\Documents\PowerBI\datos.xlsx"
in File.Contents(ruta)
⚠️ Ojo con esto: Environment.UserName devuelve el usuario del servidor de actualización en Power BI Service, no el usuario del informe. Para RLS por usuario, usa el contexto de seguridad de Power BI (USERNAME() en DAX), no esta función.
🔭

Diagnóstico y Depuración

Diagnostics.Trace / DiagnosticsExtensions

Avanzado

Sintaxis

Diagnostics.Trace(traceLevel as number, message as text, value as any, optional delayed as nullable logical) as any

¿Qué hace?

Emite un mensaje de traza al sistema de diagnósticos y devuelve el valor value sin modificarlo. Útil para depurar consultas complejas sin alterar el resultado. Los mensajes aparecen en el log de diagnósticos de Power Query.

Ejemplo

// Trazar un valor intermedio sin cambiar el resultado
let
    paso1 = some_complex_calculation(),
    traced = Diagnostics.Trace(
        TraceLevel.Information,
        "Paso 1 completado: " & Text.From(Table.RowCount(paso1)) & " filas",
        paso1
    ),
    paso2 = Table.SelectRows(traced, each [Importe] > 0)
in paso2

Table.View — Query Folding manual

Avanzado

Sintaxis

Table.View(table as nullable table, handlers as record) as table

¿Qué hace?

Table.View permite implementar query folding personalizado para conectores custom. Define handlers que se llaman cuando Power Query intenta "plegar" operaciones al origen de datos. Es la base del sistema de conectores de M.

Ejemplo

// Table.View básico con handlers de GetRows y OnSelectRows
Table.View(null, [
    GetType = () => type table [ID = number, Nombre = text],
    GetRows = () => #table({"ID","Nombre"}, {{1,"Ana"},{2,"Sara"}}),
    OnSelectRows = (selector) =>
        Table.View(null, [
            GetType = () => type table [ID = number, Nombre = text],
            GetRows = () => Table.SelectRows(#table({"ID","Nombre"}, {{1,"Ana"},{2,"Sara"}}), selector)
        ])
])
📌 Buena práctica: Table.View es para desarrollo de conectores personalizados (Custom Connectors), no para consultas cotidianas. Si necesitas entender query folding en conectores estándar, usa el diagnóstico de Power Query Desktop (View → Query Diagnostics).

Json.FromValue — Serializar a JSON

Intermedio

Sintaxis

Json.FromValue(value as any, optional encoding as nullable number) as binary

¿Qué hace?

Serializa cualquier valor M a JSON (como binario). Útil para enviar datos a APIs REST mediante POST, construir payloads dinámicos y depurar estructuras complejas.

Ejemplo

// Serializar record a JSON para POST
let
    payload = [
        usuario = "kawaii_user",
        filtros = [
            anyo    = 2024,
            region  = "ES",
            activos = true
        ]
    ],
    jsonBinario = Json.FromValue(payload),
    respuesta   = Web.Contents("https://api.ejemplo.com/datos",
        [Content  = jsonBinario,
         Headers  = [#"Content-Type" = "application/json"]])
in Json.Document(respuesta)

// Convertir a texto para depuración
Binary.ToText(Json.FromValue([A = 1, B = "dos"]), TextEncoding.Utf8)
// {"A":1,"B":"dos"}
🌸 ¡Has completado el recorrido por el Lenguaje M! Desde las funciones más básicas hasta las características más avanzadas del lenguaje. Ahora tienes todas las herramientas para transformar cualquier fuente de datos con Power Query. ¡A por ello! 🚀

Vuelve al índice de Lenguaje M o explora Funciones DAX para completar tu dominio de Power BI.