แนวคิดพื้นฐาน

ส่วนนี้จะกล่าวถึงแนวคิดพื้นฐานที่ปรากฏตลอดทั้งส่วนถัดไป

ค่า

ส่วนข้อมูลเดียวเรียกว่าค่า หากกล่าวแบบกว้างๆ แล้ว ค่ามีหมวดหมู่ทั่วไปสองหมวดหมู่: ค่าดั้งเดิม ซึ่งมีขนาดเล็กมาก และ ค่าแบบมีโครงสร้างซึ่งถูกสร้างขึ้นจากค่าดั้งเดิมและค่าแบบมีโครงสร้างอื่นๆ ตัวอย่างเช่น ค่าต่าง ๆ

1 
true
3.14159 
"abc"

เป็นค่าดั้งเดิมที่ไม่ได้สร้างขึ้นจากค่าอื่น ในทางกลับกัน ค่าดังกล่าว

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

ถูกสร้างขึ้นโดยใช้ค่าดั้งเดิมและในกรณีของเรกคอร์ด เป็นค่าแบบมีโครงสร้างอื่นๆ

นิพจน์

นิพจน์เป็นสูตรที่ใช้ในการสร้างค่าต่างๆ นิพจน์สามารถสร้างโดยใช้โครงสร้างไวยากรณ์ที่หลากหลาย ต่อไปนี้คือตัวอย่างของนิพจน์ แต่ละบรรทัดเป็นนิพจน์ที่แยกจากกัน

"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"

รูปแบบที่ง่ายที่สุดของนิพจน์ คือสัญพจน์ที่แสดงค่า ดังที่เห็นด้านบน

นิพจน์ที่ซับซ้อนมากขึ้นจะถูกสร้างขึ้นจากนิพจน์อื่น ๆ ที่เรียกว่า sub-expressions ตัวอย่างเช่น

1 + 2

จริง ๆ แล้ว นิพจน์ข้างต้นประกอบด้วยนิพจน์สามรายการ สัญ 1 พจน์ และ 2 เป็นนิพจน์ย่อยของนิพจน์ 1 + 2หลัก

การดําเนินการอัลกอริทึมที่กําหนดโดยโครงสร้างไวยากรณ์ที่ใช้ในนิพจน์เรียกว่า การประเมิน นิพจน์ นิพจน์แต่ละชนิดมีกฎสําหรับวิธีการประเมิน ตัวอย่างเช่น นิพจน์สัญพจน์อย่าง 1 จะสร้างค่าคงที่ ในขณะที่นิพจน์ a + b จะใช้ค่าผลลัพธ์ที่สร้างขึ้นโดยการประเมินนิพจน์อื่น ๆ สองรายการ (a และ b) และเพิ่มเข้าด้วยกันตามชุดกฎบางอย่าง

สภาพแวดล้อมและตัวแปร

นิพจน์จะถูกประเมินภายในสภาพแวดล้อมที่กําหนด สภาพแวดล้อมคือชุดของค่าที่มีชื่อ ที่เรียกว่าตัวแปร ตัวแปรแต่ละตัวในสภาพแวดล้อมหนึ่งๆ มีชื่อเฉพาะภายในสภาพแวดล้อมที่เรียกว่า ตัวระบุ

นิพจน์ระดับบนสุด (หรือ ราก) จะถูกประเมินภายใน สภาพแวดล้อมส่วนกลาง สภาพแวดล้อมส่วนกลางมีไว้ให้โดยตัวประเมินนิพจน์ แทนที่จะถูกกําหนดจากเนื้อหาของนิพจน์ที่ถูกประเมิน เนื้อหาของสภาพแวดล้อมส่วนกลางมีข้อกําหนดของไลบรารีมาตรฐาน และอาจได้รับผลกระทบจากการส่งออกจากส่วนจากบางชุดของเอกสาร (เพื่อความง่าย ตัวอย่างในส่วนนี้จะถือว่าเป็นสภาพแวดล้อมส่วนกลางที่ว่างเปล่า ซึ่งมีการสมมติฐานไว้ว่า ไม่มีไลบรารีมาตรฐานและไม่มีข้อกําหนดที่ยึดตามส่วนอื่นๆ)

สภาพแวดล้อมที่ใช้ในการประเมินนิพจน์ย่อยจะถูกกําหนดโดยนิพจน์หลัก ชนิดส่วนใหญ่ของนิพจน์หลักจะประเมินนิพจน์ย่อยภายในสภาพแวดล้อมเดียวกันกับที่ถูกประเมินอยู่ แต่นิพจน์บางรายการจะใช้สภาพแวดล้อมอื่น สภาพแวดล้อมส่วนกลางคือ สภาพแวดล้อม หลักภายในที่นิพจน์ส่วนกลางได้รับการประเมิน

ตัวอย่างเช่น record-initializer-expression จะประเมินนิพจน์ย่อยสําหรับแต่ละเขตข้อมูลที่มีสภาพแวดล้อมที่ปรับเปลี่ยน สภาพแวดล้อมที่ปรับเปลี่ยนมีตัวแปรสําหรับแต่ละเขตข้อมูลของเรกคอร์ด ยกเว้นเขตข้อมูลที่มีการเตรียมใช้งาน การรวมเขตข้อมูลอื่นของเรกคอร์ดจะทําให้เขตข้อมูลขึ้นอยู่กับค่าของเขตข้อมูล ตัวอย่างเช่น

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

ในทํานองเดียวกัน let-expression จะประเมินนิพจน์ย่อยสําหรับตัวแปรแต่ละรายการที่มีสภาพแวดล้อมที่มีตัวแปรแต่ละรายการของ ให้ ยกเว้นตัวแปรที่มีการเตรียมใช้งาน let-expression จะประเมินนิพจน์ที่ตามหลัง ใน ที่มีสภาพแวดล้อมที่มีตัวแปรทั้งหมด:

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

(ปรากฎว่าทั้ง record-initializer-expression และ let-expression จะกําหนด สภาพแวดล้อมสอง รายการโดยแท้จริง โดยหนึ่งในสองสภาพแวดล้อมจะหมายรวมถึงตัวแปรที่มีการเตรียมใช้งาน ข้อกําหนดนี้จะเป็นประโยชน์สําหรับข้อกําหนดแบบเรียกใช้ซ้ําขั้นสูง และครอบคลุมใน การอ้างอิงตัว ระบุ

เพื่อสร้างสภาพแวดล้อมสําหรับนิพจน์ย่อย ตัวแปรใหม่จะถูก "ผสาน" กับตัวแปรในสภาพแวดล้อมหลัก ตัวอย่างต่อไปนี้แสดงสภาพแวดล้อมสําหรับเรกคอร์ดที่ซ้อนกัน:

[
    a = 
    [ 

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

ตัวอย่างต่อไปนี้แสดงสภาพแวดล้อมสําหรับเรกคอร์ดที่ซ้อนกันภายใน ให้:

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

การผสานตัวแปรที่มีสภาพแวดล้อมอาจทําให้เกิดความขัดแย้งระหว่างตัวแปร (เนื่องจากแต่ละตัวแปรในสภาพแวดล้อมต้องมีชื่อที่ไม่ซ้ํากัน) ความขัดแย้งจะถูกแก้ไขดังนี้: ถ้าชื่อของตัวแปรใหม่ที่ผสานเหมือนกับตัวแปรที่มีอยู่ในสภาพแวดล้อมหลัก ตัวแปรใหม่จะมีความสําคัญมากขึ้นในสภาพแวดล้อมใหม่ ในตัวอย่างต่อไปนี้ ตัวแปร x ภายใน (ซ้อนกันมากขึ้น) จะมีความสําคัญเหนือกว่าตัวแปร 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
]  

การอ้างอิงตัวระบุ

identifier-reference ใช้สําหรับอ้างอิงถึงตัวแปรภายในสภาพแวดล้อมหนึ่ง ๆ

identifier-expression:
      identifier-reference
identifier-reference:
      exclusive-identifier-reference
      inclusive-identifier-reference

การอ้างอิงตัวระบุในรูปแบบที่ง่ายที่สุดคือ exclusive-identifier-reference:

exclusive-identifier-reference:
      รหัส

เป็นข้อผิดพลาดสําหรับ exclusive-identifier-reference ในการอ้างอิงถึงตัวแปรที่ไม่ใช่ส่วนหนึ่งของนิพจน์ที่ตัวระบุปรากฏอยู่

เป็นข้อผิดพลาดสําหรับ exclusive-identifier-reference ในการอ้างอิงถึงตัวระบุที่มีการเตรียมใช้งานในขณะนี้หากมีการกําหนดตัวระบุที่อ้างอิงภายใน record-initializer-expression หรือ let-expression แต่ inclusive-identifier-reference สามารถใช้เพื่อรับการเข้าถึงสภาพแวดล้อมที่มีการเตรียมใช้งานตัวระบุ หากมีการใช้ inclusive-identifier-reference ในสถานการณ์อื่น การดําเนินการนี้จะเทียบเท่ากับ exclusive-identifier-reference

inclusive-identifier-reference:
      @ตัวระบุ

การดําเนินการนี้จะเป็นประโยชน์เมื่อทําการกําหนดฟังก์ชันแบบเรียกใช้ซ้ํา เนื่องจากโดยปกติแล้ว ชื่อของฟังก์ชันจะไม่อยู่ในขอบเขต

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

    x = Factorial(5) 
]

เช่นเดียวกับ record-initializer-expression สามารถใช้ inclusive-identifier-reference ภายใน let-expression เพื่อเข้าถึงสภาพแวดล้อมที่รวมถึงตัวระบุที่มีการเตรียมใช้งาน

ลําดับการประเมิน

พิจารณานิพจน์ต่อไปนี้ที่มีการเตรียมใช้งานเรกคอร์ด:

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

เมื่อประเมิน นิพจน์นี้จะสร้างค่าเรกคอร์ดต่อไปนี้:

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

นิพจน์ จะระบุว่าเพื่อดําเนินการA + Bคํานวณสําหรับเขตข้อมูล Cต้องทราบค่าของทั้งเขตข้อมูล A และ เขตข้อมูลB นี่คือตัวอย่างของ การ เรียงลําดับแบบขึ้นต่อกันของการคํานวณจากนิพจน์ ตัวประเมิน M ต้องเป็นไปตามการเรียงลําดับแบบขึ้นต่อกันที่กําหนดโดยนิพจน์ แต่มีอิสระที่จะดําเนินการคํานวณที่เหลืออยู่ไม่ว่าในลําดับใดก็ตาม ตัวอย่างเช่น ลําดับการคํานวณอาจเป็น:

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

หรืออาจเป็น:

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

หรือ เนื่องจาก A และ B ไม่ขึ้นอยู่กับอีกตัวหนึ่ง จึงสามารถคํานวณพร้อมกันได้:

    B = 2 + 2พร้อมกันกับA = 1 + 1
    C = A + B

ผลข้างเคียง

การอนุญาตให้ตัวประเมินนิพจน์คํานวณลําดับการคํานวณโดยอัตโนมัติสําหรับกรณีที่ไม่มีการขึ้นต่อกันที่ชัดเจนที่ระบุโดยนิพจน์คือแบบจําลองการคํานวณแบบง่ายและมีประสิทธิภาพ

อย่างไรก็ตาม การดําเนินการนี้จะขึ้นอยู่กับความสามารถในการเรียงลําดับการคํานวณใหม่ เนื่องจากนิพจน์สามารถเรียกใช้ฟังก์ชันต่างๆ ได้ และฟังก์ชันเหล่านั้นอาจเห็นได้จากภายนอกในนิพจน์ดังกล่าวด้วยการออกคิวรีภายนอก เป็นไปได้ที่จะสร้างสถานการณ์สมมติที่ลําดับการคํานวณมีความสําคัญ แต่ไม่ได้ถูกบันทึกไว้ในบางลําดับของนิพจน์ดังกล่าว ตัวอย่างเช่น ฟังก์ชันหนึ่งๆ อาจอ่านเนื้อหาของไฟล์ หากฟังก์ชันดังกล่าวถูกเรียกใช้ซ้ํา ๆ อาจสามารถสังเกตการเปลี่ยนแปลงของไฟล์ดังกล่าวจากภายนอกได้ ดังนั้นการเรียงลําดับใหม่อาจทําให้เกิดความแตกต่างที่สังเกตเห็นได้ในลักษณะการทํางานของโปรแกรม ทั้งนี้ ขึ้นอยู่กับการเรียงลําดับการประเมินที่สังเกตได้สําหรับความถูกต้องของนิพจน์ M จะทําให้เกิดการขึ้นต่อกันในตัวเลือกการใช้งานเฉพาะที่อาจแตกต่างจากตัวประเมินหนึ่งกับตัวประเมินถัดไป หรืออาจแตกต่างกันในตัวประเมินเดียวกันภายใต้สถานการณ์ที่แตกต่างกัน

ความไม่สม่ําเสมอ

หลังจากคํานวณค่าแล้ว ค่าดังกล่าวจะ ไม่สามารถเปลี่ยนแปลงได้ หมายความว่าจะไม่สามารถเปลี่ยนแปลงได้อีกต่อไป การดําเนินการนี้จะทําให้ง่ายขึ้นสําหรับแบบจําลองในการประเมินนิพจน์ และทําให้ง่ายขึ้นในการให้เหตุผลเกี่ยวกับผลลัพธ์เนื่องจากเป็นไปไม่ได้ที่จะเปลี่ยนค่าเมื่อมีการใช้ในการประเมินส่วนที่ตามมาของนิพจน์ ตัวอย่างเช่น เขตข้อมูลเรกคอร์ดจะถูกคํานวณเมื่อจําเป็นเท่านั้น อย่างไรก็ตาม หลังจากคํานวณแล้ว จะยังคงคงที่ตลอดอายุการใช้งานของเรกคอร์ด แม้ว่าความพยายามในการคํานวณเขตข้อมูลจะทําให้เกิดข้อผิดพลาด ข้อผิดพลาดดังกล่าวจะเกิดขึ้นอีกครั้งเมื่อพยายามจะเข้าถึงเขตข้อมูลเรกคอร์ดนั้น

ข้อยกเว้นที่สําคัญของกฎ immutable-once-calculated จะใช้กับรายการ ตาราง และค่าไบนารี ซึ่งมี ตรรกะการสตรีม ตรรกะการสตรีมอนุญาตให้ M แปลงชุดข้อมูลที่ไม่พอดีกับหน่วยความจําทั้งหมดในครั้งเดียว ด้วยการสตรีม ค่าที่แสดงเมื่อระบุตาราง รายการ หรือค่าไบนารีที่กําหนดจะถูกสร้างขึ้นตามความต้องการในแต่ละครั้งที่มีการร้องขอ เนื่องจากนิพจน์ที่กําหนดค่าที่ระบุจะถูกประเมินในแต่ละครั้งที่มีการแจกแจง เอาต์พุตที่กําหนดอาจแตกต่างกันในการแจกแจงหลายรายการ ซึ่งไม่ได้หมายความว่าการแจกแจงหลายรายการจะส่งผลในค่าที่แตกต่างกันเสมอ แต่อาจแตกต่างกันหากแหล่งข้อมูลหรือตรรกะ M ที่ใช้ไม่มีการกําหนด

นอกจากนี้ โปรดทราบว่าแอปพลิเคชัน ฟังก์ชันไม่ เหมือนกับการสร้างค่า ฟังก์ชันไลบรารีอาจเปิดเผยสถานะภายนอก (เช่น เวลาปัจจุบันหรือผลลัพธ์ของคิวรีต่อฐานข้อมูลที่มีการพัฒนาตลอดเวลา) โดยแสดงเป็นแบบไม่มีการกําหนด ในขณะที่ฟังก์ชันที่กําหนดไว้ใน M จะไม่เปิดเผยลักษณะการทํางานแบบไม่มีการกําหนด ฟังก์ชันดังกล่าวสามารถทําได้หากถูกกําหนดไว้เพื่อเรียกใช้ฟังก์ชันอื่น ๆ ที่ไม่มีการกําหนด

แหล่งข้อมูลสุดท้ายของการไม่มีการกําหนดใน M คือ ข้อผิดพลาด ข้อผิดพลาดจะหยุดการประเมินเมื่อมีข้อผิดพลาดเกิดขึ้น (ขึ้นอยู่กับระดับที่จัดการข้อผิดพลาดดังกล่าวโดยนิพจน์ try) โดยปกติแล้วจะไม่สามารถสังเกตได้ไม่ว่าจะ a + b ทําให้เกิดการประเมิน a ก่อน b หรือ b ก่อนหน้า a (การละเว้นการเกิดซ้ําเพื่อความง่าย) อย่างไรก็ตาม หากนิพจน์ย่อยที่ถูกประเมินก่อนทําให้เกิดข้อผิดพลาด อาจมีการกําหนดนิจที่ของสองนิพจน์ถูกประเมินก่อน