Share via


Basisbegrippen

In deze sectie worden basisconcepten besproken die in de volgende secties worden weergegeven.

Waarden

Eén stukje gegevens wordt een waarde genoemd. Over het algemeen zijn er twee algemene waardencategorieën: primitieve waarden, die atomische en gestructureerde waarden zijn, die zijn opgebouwd uit primitieve waarden en andere gestructureerde waarden. Bijvoorbeeld de waarden

1 
true
3.14159 
"abc"

zijn primitieven omdat ze niet bestaan uit andere waarden. Aan de andere kant, de waarden

{1, 2, 3} 
[ A = {1}, B = {2}, C = {3} ]

worden samengesteld met behulp van primitieve waarden en, in het geval van de record, andere gestructureerde waarden.

Expressies

Een expressie is een formule die wordt gebruikt om waarden samen te stellen. Een expressie kan worden gevormd met behulp van verschillende syntactische constructies. Hier volgen enkele voorbeelden van expressies. Elke regel is een afzonderlijke expressie.

"Hello World"             // a text value 
123                       // a number 
1 + 2                     // sum of two numbers 
{1, 2, 3}                 // a list of three numbers 
[ x = 1, y = 2 + 3 ]      // a record containing two fields: 
                          //        x and y 
(x, y) => x + y           // a function that computes a sum 
if 2 > 1 then 2 else 1    // a conditional expression 
let x = 1 + 1  in x * 2   // a let expression 
error "A"                 // error with message "A"

De eenvoudigste vorm van expressie, zoals hierboven wordt weergegeven, is een letterlijke waarde die een waarde vertegenwoordigt.

Complexere expressies worden gebouwd op basis van andere expressies, subexpressies genoemd. Bijvoorbeeld:

1 + 2

De bovenstaande expressie bestaat uit drie expressies. De 1 en 2 letterlijke waarden zijn subexpressies van de bovenliggende expressie 1 + 2.

Het uitvoeren van het algoritme dat is gedefinieerd door de syntactische constructies die in een expressie worden gebruikt, wordt het evalueren van de expressie genoemd. Elk type expressie bevat regels voor hoe deze wordt geëvalueerd. Een letterlijke expressie, zoals 1 , produceert bijvoorbeeld een constante waarde, terwijl de expressie a + b de resulterende waarden neemt die worden geproduceerd door twee andere expressies (a en b) te evalueren en deze samen te voegen volgens een aantal regels.

Omgevingen en variabelen

Expressies worden geëvalueerd binnen een bepaalde omgeving. Een omgeving is een set benoemde waarden, variabelen genoemd. Elke variabele in een omgeving heeft een unieke naam in de omgeving die een id wordt genoemd.

Er wordt een expressie op het hoogste niveau (of hoofd) geëvalueerd in de globale omgeving. De globale omgeving wordt geleverd door de expressie-evaluator in plaats van te worden bepaald op basis van de inhoud van de expressie die wordt geëvalueerd. De inhoud van de globale omgeving bevat de standaardbibliotheekdefinities en kan worden beïnvloed door exports uit secties uit een aantal documenten. (Voor het gemak gaan de voorbeelden in deze sectie uit van een lege globale omgeving. Dat wil gezegd, wordt ervan uitgegaan dat er geen standaardbibliotheek is en dat er geen andere definities op basis van secties zijn.)

De omgeving die wordt gebruikt om een subexpressie te evalueren, wordt bepaald door de bovenliggende expressie. De meeste bovenliggende expressietypen evalueren een subexpressie binnen dezelfde omgeving waarbinnen ze zijn geëvalueerd, maar sommige gebruiken een andere omgeving. De globale omgeving is de bovenliggende omgeving waarin de globale expressie wordt geëvalueerd.

De record-initializer-expression evalueert bijvoorbeeld de subexpressie voor elk veld met een gewijzigde omgeving. De gewijzigde omgeving bevat een variabele voor elk van de velden van de record, behalve de omgeving die wordt geïnitialiseerd. Door de andere velden van de record op te nemen, kunnen de velden afhankelijk zijn van de waarden van de velden. Bijvoorbeeld:

[  
    x = 1,          // environment: y, z 
    y = 2,          // environment: x, z 
    z = x + y       // environment: x, y
] 

Op dezelfde manier evalueert de let-expression de subexpressie voor elke variabele met een omgeving die elk van de variabelen van de let bevat, behalve de variabele die wordt geïnitialiseerd. De let-expression evalueert de expressie na de in met een omgeving die alle variabelen bevat:

let 

    x = 1,          // environment: y, z 
    y = 2,          // environment: x, z 
    z = x + y       // environment: x, y
in
    x + y + z       // environment: x, y, z

(Het blijkt dat zowel record-initializer-expression als let-expression eigenlijk twee omgevingen definiëren, waaronder een van de variabelen die worden geïnitialiseerd. Dit is handig voor geavanceerde recursieve definities en wordt behandeld in id-verwijzingen .

Als u de omgevingen voor de subexpressies wilt vormen, worden de nieuwe variabelen 'samengevoegd' met de variabelen in de bovenliggende omgeving. In het volgende voorbeeld ziet u de omgevingen voor geneste records:

[
    a = 
    [ 

        x = 1,      // environment: b, y, z 
        y = 2,      // environment: b, x, z 
        z = x + y   // environment: b, x, y 
    ], 
    b = 3           // environment: a
]  

In het volgende voorbeeld ziet u de omgevingen voor een record die is genest binnen een let:

Let
    a =
    [
        x = 1,       // environment: b, y, z 
        y = 2,       // environment: b, x, z 
        z = x + y    // environment: b, x, y 
    ], 
    b = 3            // environment: a 
in 
    a[z] + b         // environment: a, b

Het samenvoegen van variabelen met een omgeving kan een conflict veroorzaken tussen variabelen (omdat elke variabele in een omgeving een unieke naam moet hebben). Het conflict wordt als volgt opgelost: als de naam van een nieuwe variabele die wordt samengevoegd, hetzelfde is als een bestaande variabele in de bovenliggende omgeving, heeft de nieuwe variabele voorrang in de nieuwe omgeving. In het volgende voorbeeld heeft de binnenste variabele x (dieper genest) voorrang op de buitenste variabele x.

[
    a =
    [ 
        x = 1,       // environment: b, x (outer), y, z 
        y = 2,       // environment: b, x (inner), z 
        z = x + y    // environment: b, x (inner), y 
    ], 
    b = 3,           // environment: a, x (outer) 
    x = 4            // environment: a, b
]  

Id-verwijzingen

Een id-verwijzing wordt gebruikt om te verwijzen naar een variabele in een omgeving.

id-expressie:
      id-verwijzing
id-verwijzing:
      exclusive-identifier-reference
      inclusive-identifier-reference

De eenvoudigste vorm van id-verwijzing is een exclusieve-id-verwijzing:

exclusive-identifier-reference:
      Id

Het is een fout voor een exclusieve-id-verwijzing om te verwijzen naar een variabele die geen deel uitmaakt van de omgeving van de expressie waarbinnen de id wordt weergegeven.

Het is een fout voor een exclusieve-id-verwijzing om te verwijzen naar een id die momenteel wordt geïnitialiseerd als de id waarnaar wordt verwezen, is gedefinieerd in een record-initializer-expression of let-expression. In plaats daarvan kan een inclusieve-id-verwijzing worden gebruikt om toegang te krijgen tot de omgeving waarin de id wordt geïnitialiseerd. Als er in een andere situatie een inclusieve-id-verwijzing wordt gebruikt, is deze gelijk aan een exclusieve-id-verwijzing.

inclusive-identifier-reference:
      @id

Dit is handig bij het definiëren van recursieve functies, omdat de naam van de functie normaal gesproken niet binnen het bereik valt.

[ 
    Factorial = (n) =>
        if n <= 1 then
            1
        else
            n * @Factorial(n - 1),  // @ is scoping operator

    x = Factorial(5) 
]

Net als bij een record-initializer-expression kan een inclusieve-id-verwijzing worden gebruikt in een let-expressie om toegang te krijgen tot de omgeving waarin de id wordt geïnitialiseerd.

Volgorde van evaluatie

Bekijk de volgende expressie waarmee een record wordt geïnitialiseerd:

[ 
    C = A + B, 
    A = 1 + 1, 
    B = 2 + 2 
]

Wanneer deze expressie wordt geëvalueerd, produceert deze expressie de volgende recordwaarde:

[ 
    C = 6, 
    A = 2, 
    B = 4 
]

De expressie geeft aan dat om de A + B berekening voor het veld Cuit te voeren, de waarden van zowel het veld A als het veld B bekend moeten zijn. Dit is een voorbeeld van een afhankelijkheidsvolgorde van berekeningen die wordt geleverd door een expressie. De M-evaluator houdt zich aan de afhankelijkheidsvolgorde van expressies, maar is vrij om de resterende berekeningen uit te voeren in elke gewenste volgorde. De rekenvolgorde kan bijvoorbeeld het volgende zijn:

A = 1 + 1 
B = 2 + 2 
C = A + B

Of het kan het volgende zijn:

B = 2 + 2 
A = 1 + 1 
C = A + B

Of omdat A en B niet van elkaar afhankelijk zijn, kunnen ze gelijktijdig worden berekend:

    B = 2 + 2gelijktijdig metA = 1 + 1
    C = A + B

Bijwerkingen

Een expressie-evaluator toestaan om automatisch de volgorde van berekeningen te berekenen voor gevallen waarin de expressie geen expliciete afhankelijkheden bevat, is een eenvoudig en krachtig rekenmodel.

Het is echter afhankelijk van het opnieuw ordenen van berekeningen. Omdat expressies functies kunnen aanroepen en deze functies de status buiten de expressie kunnen observeren door externe query's uit te geven, is het mogelijk om een scenario te maken waarin de volgorde van de berekening van belang is, maar niet wordt vastgelegd in de gedeeltelijke volgorde van de expressie. Een functie kan bijvoorbeeld de inhoud van een bestand lezen. Als deze functie herhaaldelijk wordt aangeroepen, kunnen externe wijzigingen in dat bestand worden geobserveerd en kan het herschikken daarom waarneembare verschillen veroorzaken in het gedrag van het programma. Afhankelijk van een dergelijke waargenomen evaluatievolgorde voor de juistheid van een M-expressie veroorzaakt een afhankelijkheid van bepaalde implementatiekeuzes die kunnen variëren van de ene evaluator tot de volgende of zelfs kunnen verschillen op dezelfde evaluator onder verschillende omstandigheden.

Onveranderbaarheid

Zodra een waarde is berekend, is deze onveranderbaar, wat betekent dat deze niet meer kan worden gewijzigd. Dit vereenvoudigt het model voor het evalueren van een expressie en maakt het gemakkelijker om redeneren over het resultaat, omdat het niet mogelijk is om een waarde te wijzigen zodra deze is gebruikt om een volgend deel van de expressie te evalueren. Een recordveld wordt bijvoorbeeld alleen berekend wanneer dat nodig is. Eenmaal berekend, blijft deze echter vast voor de levensduur van de record. Zelfs als de poging om het veld te berekenen een fout heeft gegenereerd, wordt dezelfde fout opnieuw gegenereerd bij elke poging om toegang te krijgen tot dat recordveld.

Een belangrijke uitzondering op de onveranderbare en eenmaal berekende regel is van toepassing op lijst-, tabel- en binaire waarden, die streaming-semantiek hebben. Met streaming-semantiek kan M gegevenssets transformeren die niet in het geheugen passen. Met streaming worden de waarden die worden geretourneerd bij het inventariseren van een bepaalde tabel, lijst of binaire waarde, op aanvraag geproduceerd telkens wanneer ze worden aangevraagd. Omdat de expressies die de geïnventareerde waarden definiëren telkens worden geëvalueerd wanneer ze worden geïnventariseerd, kan de uitvoer die ze produceren verschillen tussen meerdere opsommingen. Dit betekent niet dat meerdere opsommingen altijd verschillende waarden opleveren, alleen dat ze kunnen verschillen als de gebruikte gegevensbron of M-logica niet-deterministisch is.

Houd er ook rekening mee dat de functietoepassing niet hetzelfde is als waardeconstructie. Bibliotheekfuncties kunnen externe status (zoals de huidige tijd of de resultaten van een query in een database die zich na verloop van tijd ontwikkelt) beschikbaar maken, waardoor ze niet-deterministisch worden weergegeven. Hoewel functies die in M zijn gedefinieerd, geen dergelijk niet-deterministisch gedrag blootstellen, kunnen ze als ze zijn gedefinieerd om andere functies aan te roepen die niet-deterministisch zijn.

Een laatste bron van niet-determinisme in M zijn fouten. Fouten stoppen evaluaties wanneer ze optreden (tot het niveau waarop ze worden verwerkt door een try-expressie). Het is normaal gesproken niet waarneembaar of a + b de evaluatie van a vóór b of b vóór a werd veroorzaakt (waarbij gelijktijdigheid hier ter vereenvoudiging wordt genegeerd). Als de subexpressie die eerst is geëvalueerd, echter een fout genereert, kan worden bepaald welke van de twee expressies eerst is geëvalueerd.