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