- กฎการต่อสู้ของ Pokémon มีลักษณะใกล้เคียงกับ rule engine ที่มีกฎอย่างความได้เปรียบเสียเปรียบของประเภท ท่า ค่าสเตตัส และความสามารถเชื่อมโยงกัน จึงสามารถแสดงอย่างกระชับด้วยโมเดลความสัมพันธ์และกฎของ Prolog
- Prolog เก็บข้อเท็จจริงด้วย predicate อย่าง
pokemon/1, type/2 และใช้ ตัวแปร ที่ขึ้นต้นด้วยอักษรตัวใหญ่ร่วมกับการรวมค่าเพื่อค้นหา Pokémon ที่ตรงตามเงื่อนไขด้านประเภทหรือท่า
- การค้นหา Pokémon ที่เรียน Freeze-Dry ได้ เป็นประเภท Ice และมี Special Attack มากกว่า 120 สามารถเขียนเป็นคำสั่งถามใน Prolog ได้สั้นกว่าการใช้
EXISTS หลายชั้นใน SQL
- ทีมดราฟต์สามารถแสดงด้วย predicate อย่าง
alex/1, morry/1 และในกฎของ ท่าที่มีลำดับความสำคัญ ก็สามารถค่อย ๆ เพิ่มเงื่อนไขยกเว้นและเอฟเฟกต์ของ Prankster เข้าไปเป็นชั้น ๆ ได้
- สเปรดชีตอย่าง Techno's Prep Doc นั้นทรงพลัง แต่ ฐานข้อมูล Prolog ยืดหยุ่นกว่าสำหรับการ query แบบผสมตามต้องการ และถูกนำไปทำจริงด้วย prologdex และ Scryer Prolog
ทำไมกฎการต่อสู้ของ Pokémon จึงเหมาะกับการเขียนโปรแกรมเชิงตรรกะ
- การต่อสู้ของ Pokémon มีลักษณะใกล้เคียงกับ rule engine ที่มีกฎจำนวนมากเชื่อมโยงกันอย่างซับซ้อน และการเขียนโปรแกรมเชิงตรรกะแบบ Prolog ก็เหมาะกับการแสดงความสัมพันธ์แบบนี้อย่างกระชับ
- Pokémon คือคาแรกเตอร์ที่มีชื่อสายพันธุ์ ตั้งแต่ Bulbasaur #1) ไปจนถึง Pecharunt #1025) รวมแล้วมากกว่า 1,000 สายพันธุ์
- การต่อสู้ในซีรีส์หลักเป็นการสู้กันระหว่างทีมที่มี 6 ตัว โดย Pokémon แต่ละตัวมักเลือกใช้หนึ่งใน 4 ท่า ซึ่งโดยทั่วไปใช้สร้างความเสียหายแก่คู่ต่อสู้ และจะชนะเมื่อทำให้ HP ของทีมตรงข้ามเหลือ 0 ทั้งหมด
- ประสิทธิภาพในการต่อสู้ขึ้นอยู่กับค่าสเตตัสพื้นฐาน รายชื่อท่าที่เรียนได้ ความสามารถ และ ประเภท ทำให้จำนวนชุดผสมมีมากและคุ้มค่าที่จะติดตามด้วยซอฟต์แวร์
- ประเภทมีทั้งฝั่งท่าและฝั่ง Pokémon และถ้าประเภทของท่าได้เปรียบประเภทของเป้าหมาย จะสร้างความเสียหาย 2 เท่า แต่ถ้าเสียเปรียบจะสร้างความเสียหาย 1/2 เท่า
- ตัวคูณของประเภทสามารถสะสมกันได้
- Scizor) เป็นประเภท Bug/Steel และทั้งสองประเภทแพ้ Fire จึงได้รับความเสียหายจากท่า Fire เป็น 4 เท่า
- หากใช้ท่า Electric ใส่ Swampert) ซึ่งเป็นประเภท Water/Ground ความเสียหายจะเป็น 0 เพราะความต้านทานแบบไม่รับผลของ Ground
โมเดลพื้นฐานของ Prolog
- ใน Prolog จะประกาศความสัมพันธ์ด้วย predicate
pokemon(bulbasaur).
pokemon(ivysaur).
pokemon(venusaur).
pokemon(charmander).
pokemon(charmeleon).
pokemon(charizard).
pokemon(squirtle).
pokemon(wartortle).
pokemon(blastoise).
pokemon/1 คือ predicate ที่มีชื่อ pokemon และมีอาร์กิวเมนต์หนึ่งตัว และ query อย่าง pokemon(squirtle). จะตรวจสอบว่าสามารถทำให้ประโยคนั้นเป็นจริงได้หรือไม่
?- pokemon(squirtle).
true.
?- pokemon(alex).
false.
- ประเภทของ Pokémon สามารถแสดงเป็นความสัมพันธ์ที่มีสองอาร์กิวเมนต์อย่าง
type/2 และ Pokémon ที่มีสองประเภทก็จะมีข้อเท็จจริง type สองบรรทัดสำหรับ Pokémon ตัวเดียวกัน
type(bulbasaur, grass).
type(bulbasaur, poison).
type(charmander, fire).
type(charizard, fire).
type(charizard, flying).
type(squirtle, water).
- ชื่อที่ขึ้นต้นด้วยตัวใหญ่คือ ตัวแปร และ Prolog จะพยายามรวมค่า (unify) query ที่มีตัวแปรเข้ากับค่าที่เป็นไปได้ทั้งหมด
?- type(squirtle, Type).
Type = water.
?- type(venusaur, Type).
Type = grass
; Type = poison.
- หากใส่อาร์กิวเมนต์ตัวแรกเป็นตัวแปรแบบ
type(Pokemon, grass). ก็จะสามารถค้นหา Pokémon ประเภท Grass ทั้งหมดได้ และในข้อมูลจริงจะได้ผลลัพธ์ 164 รายการ
- เครื่องหมายจุลภาคหมายถึงต้องเป็นไปตาม predicate หลายตัวพร้อมกัน และชื่อตัวแปรเดียวกันต้องมีค่าเดียวกันภายใน query นั้น
?- type(Pokemon, water), type(Pokemon, ice).
Pokemon = dewgong
; Pokemon = cloyster
; Pokemon = lapras
; Pokemon = laprasgmax
; Pokemon = spheal
; Pokemon = sealeo
; Pokemon = walrein
; Pokemon = arctovish
; Pokemon = ironbundle
; false.
- อย่าง Iron Bundle) ค่าสเตตัสและรายชื่อท่าที่เรียนได้ก็สามารถ query เป็นความสัมพันธ์ได้เช่นกัน
?- pokemon_spa(ironbundle, SpA).
SpA = 124.
?- learns(ironbundle, Move), move_category(Move, special).
Move = aircutter
; Move = blizzard
; Move = chillingwater
; Move = freezedry
; Move = hydropump
; Move = hyperbeam
; Move = icebeam
; Move = icywind
; Move = powdersnow
; Move = swift
; Move = terablast
; Move = waterpulse
; Move = whirlpool.
- หากผสมเงื่อนไขอย่าง
SpA #> 120 เข้าไป ก็จะค้นหา Pokémon ที่มี Special Attack มากกว่า 120 เรียน Freeze-Dry ได้ และเป็นประเภท Ice ได้ทันที
?- pokemon_spa(Pokemon, SpA), SpA #> 120, learns(Pokemon, freezedry), type(Pokemon, ice).
Pokemon = glaceon, SpA = 130
; Pokemon = kyurem, SpA = 130
; Pokemon = kyuremwhite, SpA = 170
; Pokemon = ironbundle, SpA = 124
; false.
- กฎ (rule) ของ Prolog ประกอบด้วยส่วนหัวและส่วนเนื้อหา และถ้าส่วนเนื้อหาเป็นจริง ส่วนหัวก็จะรวมค่าได้เช่นกัน
damaging_move(Move) :-
move_category(Move, physical)
; move_category(Move, special).
- กฎนี้จัดประเภทท่า Physical หรือ Special ให้เป็นท่าที่สร้างความเสียหายโดยตรง
?- damaging_move(tackle).
true.
?- damaging_move(rest).
false.
รูปแบบการเขียนคำสั่งค้นหาที่มักถูกเทียบกับ SQL
- ตัวอย่างจนถึงตอนนี้ในเชิงตรรกะเป็นเพียงการผสม
and และ or แบบง่าย ๆ แต่ใน Prolog การค้นหาความสัมพันธ์จะสั้นกว่า SQL และแก้ไขได้ง่ายกว่า
- หากจัดข้อมูลเดียวกันด้วย SQL ก็สามารถแยก Pokémon, ประเภท และท่า ไว้ในตารางคนละตัวได้
CREATE TABLE pokemon (pokemon_name TEXT, special_attack INTEGER);
CREATE TABLE pokemon_types(pokemon_name TEXT, type TEXT);
CREATE TABLE pokemon_moves(pokemon_name TEXT, move TEXT, category TEXT);
- หากต้องการหา Pokémon ที่เรียน Freeze-Dry ได้ เป็นประเภท Ice และมี Special Attack มากกว่า 120 ด้วย SQL จะต้องใช้
EXISTS หลายครั้ง
SELECT DISTINCT pokmeon, special_attack
FROM pokemon as p
WHERE
p.special_attack > 120
AND EXISTS (
SELECT 1
FROM pokemon_moves as pm
WHERE p.pokemon_name = pm.pokemon_name AND move = 'freezedry'
)
AND EXISTS (
SELECT 1
FROM pokemon_types as pt
WHERE p.pokemon_name = pt.pokemon_name AND type = 'ice'
);
- คำสั่งค้นหาแบบเดียวกันใน Prolog จะเพียงแค่เรียงความสัมพันธ์ที่ต้องการออกมาตรง ๆ
?- pokemon_spa(Pokemon, SpA),
SpA #> 120,
learns(Pokemon, freezedry),
type(Pokemon, ice).
- เมื่อมีการเพิ่มเงื่อนไขต่อเนื่อง คำสั่งค้นหา SQL มักซับซ้อนขึ้นได้ง่าย แต่คำสั่งค้นหา Prolog จะยังคงรูปแบบที่อ่านและแก้ไขได้ง่าย หากคุ้นเคยกับการทำงานของตัวแปรแล้ว
วิธีซ้อนกฎการต่อสู้เป็นชั้น ๆ
- การต่อสู้ของ Pokémon มีกฎการโต้ตอบกันอยู่มากมาย เช่น การโจมตีพลาด การเพิ่ม·ลดค่าสถานะ ผลของไอเท็ม ช่วงความเสียหาย สถานะผิดปกติ เอฟเฟกต์สนามอย่างสภาพอากาศ·ภูมิประเทศ·Trick Room ความสามารถ และการกระจายค่าสถานะพื้นฐานล่วงหน้า
- เมื่อต้องสร้างซอฟต์แวร์สำหรับ Pokémon จำเป็นต้องรับมือกับความซับซ้อนนี้ พร้อมทั้งทำให้โมเดลยังอยู่ในรูปแบบที่จัดการได้
- Prolog มีจุดแข็งทั้งในด้านโมเดลการค้นหาที่ใช้อธิบายการผสมกันเฉพาะหน้า และการจัดชั้นกฎอย่างสม่ำเสมอ
- สามารถดูความซับซ้อนลักษณะนี้ได้โดยตรงจาก damage calculator
ดราฟต์ลีกและการค้นหาท่าที่มีลำดับความสำคัญ
- ใน Pokémon Draft จะมีการกำหนดมูลค่าให้ Pokémon แต่ละตัว และผู้เล่นจะเลือก Pokémon ภายในแต้มที่กำหนดเพื่อจัดทีมราว 8~11 ตัว
- เนื่องจากการต่อสู้จริงเป็นแบบ 6v6 การเตรียมตัวรับมือชุดผสมหกตัวที่คู่ต่อสู้อาจนำมา และการเลือกหกตัวที่จะใช้สู้ จึงเป็นเรื่องสำคัญ
- Pokémon ที่ตนเองดราฟต์มาแล้วสามารถแสดงตรง ๆ ได้ด้วยเพรดิเคตอย่าง
alex/1
alex(meowscarada).
alex(weezinggalar).
alex(swampertmega).
alex(latios).
alex(volcarona).
alex(tornadus).
alex(politoed).
alex(archaludon).
alex(beartic).
alex(dusclops).
- คำสั่งค้นหา Pokémon ในทีมนี้ที่เรียน Freeze-Dry ได้ทำได้ง่าย แต่จะไม่พบผลลัพธ์
?- alex(Pokemon), learns(Pokemon, freezedry).
false.
- ลำดับการต่อสู้โดยพื้นฐานถูกกำหนดด้วย Speed แต่แต่ละท่าจะมีลำดับความสำคัญ(priority) และท่าที่มีลำดับความสำคัญสูงกว่าจะออกก่อน
- ท่าส่วนใหญ่มีลำดับความสำคัญเป็น 0 แต่ท่าที่มีลำดับความสำคัญ 1 อย่าง Accelerock จะออกก่อนท่าลำดับความสำคัญ 0 ของ Pokémon ที่เร็วกว่า
- ท่าที่ Pokémon ตัวหนึ่งเรียนได้และมีลำดับความสำคัญเป็นบวก สามารถค้นหาได้โดยรวม
learns/2, move_priority/2 และเงื่อนไขของลำดับความสำคัญเข้าด้วยกัน
- คำสั่งค้นหาแบบตรงไปตรงมาจะรวมทั้งท่าอย่าง Helping Hand และ Ally Switch ที่มีความหมายมากใน Double Battles หรือท่าอย่าง Bide ที่แทบไม่มีความหมายในการเล่นจริงด้วย
\+/1 จะเป็นจริงเมื่อเป้าหมายล้มเหลว และ dif/2 หมายถึงสองพจน์แตกต่างกัน ดังนั้นจึงสามารถเพิ่มกฎเพื่อคัดท่าสำหรับ Double Battles และ Bide ออกได้
learns_priority(Mon, Move, Priority) :-
learns(Mon, Move),
\+ doubles_move(Move),
dif(Move, bide),
move_priority(Move, Priority),
Priority #> 0.
- หากตัดท่าเชิงป้องกันอย่าง Protect, Detect, Endure และ Magic Coat ออกด้วย ก็จะเหลือเฉพาะท่าที่มีลำดับความสำคัญซึ่งสามารถสร้างความเสียหายหรือผลด้านลบให้คู่ต่อสู้ได้จริง
?- alex(Pokemon), learns_priority(Pokemon, Move, Priority).
Pokemon = meowscarada, Move = quickattack, Priority = 1
; Pokemon = meowscarada, Move = suckerpunch, Priority = 1
; Pokemon = beartic, Move = aquajet, Priority = 1
; Pokemon = dusclops, Move = shadowsneak, Priority = 1
; Pokemon = dusclops, Move = snatch, Priority = 4
; Pokemon = dusclops, Move = suckerpunch, Priority = 1
; false.
- หากนำกฎเดียวกันไปใช้กับเพรดิเคตของทีมคู่ต่อสู้ ก็สามารถหาท่าที่มีลำดับความสำคัญของฝั่งตรงข้ามได้ทันที
?- morry(Pokemon), learns_priority(Pokemon, Move, Priority).
Pokemon = mawilemega, Move = snatch, Priority = 4
; Pokemon = mawilemega, Move = suckerpunch, Priority = 1
; Pokemon = walkingwake, Move = aquajet, Priority = 1
; Pokemon = ursaluna, Move = babydolleyes, Priority = 1
; Pokemon = lokix, Move = feint, Priority = 2
; Pokemon = lokix, Move = firstimpression, Priority = 2
; Pokemon = lokix, Move = suckerpunch, Priority = 1
; Pokemon = alakazam, Move = snatch, Priority = 4
; Pokemon = skarmory, Move = feint, Priority = 2
; Pokemon = froslass, Move = iceshard, Priority = 1
; Pokemon = froslass, Move = snatch, Priority = 4
; Pokemon = froslass, Move = suckerpunch, Priority = 1
; Pokemon = dipplin, Move = suckerpunch, Priority = 1.
ขยายความสามารถ Prankster
- Pokémon ที่มีความสามารถ Prankster จะได้รับลำดับความสำคัญของท่าสถานะเพิ่มอีก +1 และเอฟเฟกต์นี้ก็สามารถนำไปบวกเพิ่มในกฎ
learns_priority/3 เดิมได้
- ภายในทีม Tornadus มีความสามารถ Prankster
?- alex(Pokemon), pokemon_ability(Pokemon, prankster).
Pokemon = tornadus
; false.
- สามารถใช้ไวยากรณ์ if/then ของ Prolog คือ
->/2 เพื่อให้ถ้า Pokémon เป็น Prankster และหมวดของท่าเป็น status ก็เพิ่ม 1 ให้กับลำดับความสำคัญพื้นฐาน มิฉะนั้นก็ใช้ค่าลำดับความสำคัญพื้นฐานเดิม
learns_priority(Mon, Move, Priority) :-
learns(Mon, Move),
\+ doubles_move(Move),
\+ protection_move(Move),
Move \= bide,
move_priority(Move, BasePriority),
(
pokemon_ability(Mon, prankster), move_category(Move, status) ->
Priority #= BasePriority + 1
; Priority #= BasePriority
),
Priority #> 0.
- หลังจากเพิ่มกฎนี้ คำสืบค้นเดิมจะรวมท่าสถานะของ Tornadus อย่าง Agility, Defog, Nasty Plot, Rain Dance, Tailwind, Taunt และ Toxic ไว้ที่ลำดับความสำคัญ 1
- การขยายกฎเพียงข้อเดียวก็สะท้อนเอฟเฟกต์ของความสามารถได้ด้วย จึงแสดงให้เห็นข้อดีของการจัดเป็นเลเยอร์ใน Prolog
เปรียบเทียบกับเครื่องมือที่อิงสเปรดชีต
- ในคอมมูนิตี้ Pokémon มีรีซอร์สสำหรับค้นหาข้อมูลอย่างท่าที่มีลำดับความสำคัญของทีมคู่ต่อสู้อยู่แล้ว โดยตัวอย่างที่เด่นคือ Google Sheets ขั้นสูงอย่าง “Techno’s Prep Doc”
- สเปรดชีตนี้สามารถสร้างข้อมูลแมตช์อัปได้มากมายเมื่อใส่ทีมลงไป พร้อมรองรับหลายฟอร์แมต มีภาพประกอบที่ไล่ดูได้ง่าย และมีระบบเติมข้อความอัตโนมัติ
- สูตรที่ใช้ค้นหาท่าที่มีลำดับความสำคัญนั้นผสม
FILTER, VLOOKUP และ INDIRECT เข้าด้วยกัน โดย INDIRECT จะคืนค่าการอ้างอิงเซลล์
={IFERROR(ARRAYFORMULA(VLOOKUP(FILTER(INDIRECT(Matchup!$S$3&"!$AV$4:$AV"),INDIRECT(Matchup!$S$3&"!$AT$4:$AT")="X"),{Backend!$L$2:$L,Backend!$F$2:$F},2,FALSE))),IFERROR(FILTER(INDIRECT(Matchup!$S$3&"!$AW$4:$AW"),INDIRECT(Matchup!$S$3&"!$AT$4:$AT")="X"))}
- ในชีต Backend มีการไล่รายการท่าทั้งหมดไว้ และโครงสร้างนี้ก็ใกล้เคียงกับเวอร์ชันที่ฮาร์ดโค้ดคำสืบค้น Prolog เอาไว้
- ฐานข้อมูล Prolog ขยายต่อได้ง่ายกว่าวิธีฮาร์ดโค้ดรายการท่าที่น่าสนใจ และสามารถค้นหาท่าไหนก็ได้
- ยังสามารถเขียนคำถามแบบผสมที่ไม่มีในเครื่องมือเดิมได้อย่างสั้น เช่น การหาท่า Special ที่ Tornadus เรียนได้และมีผล super effective ต่อสมาชิกในทีมของ Justin
?- justin(Target), learns(tornadus, Move), super_effective_move(Move, Target), move_category(Move, special).
Target = charizardmegay, Move = chillingwater
; Target = terapagosterastal, Move = focusblast
; Target = alomomola, Move = grassknot
; Target = scizor, Move = heatwave
; Target = scizor, Move = incinerate
; Target = runerigus, Move = chillingwater
; Target = runerigus, Move = darkpulse
; Target = runerigus, Move = grassknot
; Target = runerigus, Move = icywind
; Target = screamtail, Move = sludgebomb
; Target = screamtail, Move = sludgewave
; Target = trapinch, Move = chillingwater
; Target = trapinch, Move = grassknot
; Target = trapinch, Move = icywind
; false.
หมายเหตุด้านการใช้งานจริงและข้อจำกัด
1 ความคิดเห็น
ความคิดเห็นจาก Lobste.rs
สงสัยว่ามีใครใช้ Prolog แบบใช้งานจริงให้เกิดประโยชน์อยู่ไหม จะใช้ในงานหรือใช้ส่วนตัวก็ได้ เพราะจนถึงตอนนี้ผมเห็นแต่ตัวอย่างของเล่นแบบนี้
พูดให้เคร่งครัดก็คือผมมีโค้ด Prolog ที่รันใช้งานจริงอยู่มากกว่าหนึ่งตัว เช่นแดชบอร์ดวิเคราะห์ภายใน และเมื่อก่อนผมก็เคยเขียนแบ็กเอนด์เซิร์ฟเวอร์ของแอป iOS ด้วย Prolog ด้วย ระหว่างนั้นก็เลยได้ทำไลบรารี HTTP/2 client สำหรับ Prolog ขึ้นมา เพื่อส่งการแจ้งเตือน APNS โดยไม่ต้องพึ่งบริการภายนอก
ในชุมชน Prolog แน่นอนว่ามีคนที่ชอบเอาไปใช้ทำอย่างเว็บเซิร์ฟเวอร์ แต่สำหรับผมมันใกล้เคียงกับการปลดสกิลทรีอีกสายมากกว่า เช่นทำให้มองออกดีขึ้นว่าปัญหาแบบไหนเหมาะกับ parser เฉพาะทางหรือ DSL
จากความรู้ที่ได้หลังเขียนบทความนี้ ผมได้นำ ส่วนย่อยที่มีประโยชน์ ของ tax logic engine ใน IRS Fact Graph มาเขียนใหม่ Prolog เหมาะกับงานนี้อย่างน่าประหลาด เพราะมันเผยให้เห็นมุมที่ไม่มีการทำเอกสารไว้และบังคับให้ต้องแก้ ซึ่งใน implementation แบบ imperative มักจะปล่อยผ่านไปได้
ส่วนของ “execution” ยังทำไม่เสร็จ แต่ฝั่ง parsing คืบหน้าไปมากพอจนเขียนร่างเอกสารที่ใช้ได้ค่อนข้างดีแล้ว ตอนนี้ยังขาดฟีเจอร์ใหญ่อย่างหนึ่งคือ date arithmetic และถ้าใส่ส่วนนั้นได้แล้วคงจะเขียนแยกอีกที
ถ้าใช้ DCG แล้ว Prolog จะยอดเยี่ยมมากสำหรับการ parse ข้อความเชิงโครงสร้างที่ซับซ้อน เมื่อก่อนถ้า awk เอาไม่อยู่ ผมมักจะนึกถึง Python หรือ JS แต่ตอนนี้ถ้างานต้องการโครงสร้างและระเบียบ Prolog จะเหมาะมาก มันยังให้ความรู้สึกน่าพอใจแบบสมัยเก่าที่เขียนโค้ดเบสซับซ้อนลงในไฟล์เดียว และก็ไม่ได้ย่อเกินไปแบบ APL
ตัวอย่างอาจดูเล็กน้อย แต่กรณี Pokémon ไม่ได้เล็กน้อยเลย ตัวอย่างหลายอย่างที่ดูเหมือนเล็กน้อยนั้นทำได้เพราะมี โค้ดที่ implement กลไกการต่อสู้อันซับซ้อนอย่างน่าขันไว้อย่างละเอียดมาก อยู่แล้ว ผมสนใจจะสร้าง Prolog rules engine ที่ทำงานบางส่วนแทนเครื่องมือเดิม และก็ลองทำทีละนิด ข้อดีคือเมื่อเทียบกับโค้ดแบบ imperative แล้ว การค้นหาแบบ depth-first ช่วยให้เผยความเป็นไปได้ต่าง ๆ ได้ง่ายกว่าและดูแลรักษาได้ดีกว่า
เอกสารของ Scryer Prolog ก็สร้างด้วยโปรแกรม Prolog ที่ผมเรียกว่า DocLog ผมยังทำไลบรารีบางตัวที่โปรแกรมเหล่านี้ใช้อีกด้วย ส่วน SWI Prolog เองก็ใช้ SWI โดยตรงกับเว็บไซต์ของตัวเอง, เว็บ IDE ที่ชื่อ Swish และเซิร์ฟเวอร์ ClioPatria เป็นต้น
ผมเคยใช้ SQLite คล้าย ๆ สเปรดชีตที่ปลอดภัยด้านชนิดข้อมูล เพื่อจัดระเบียบข้อมูล และทำได้แม้จะไม่มี CRUD interface ครอบอยู่เลย
แต่ก็ต้องยอมรับว่ามันไม่ใช่ภาษาที่อ่านสวยที่สุดเสมอไป ผมก็เคยมอง Datalog อยู่พักหนึ่ง แต่ implementation ส่วนใหญ่ดูเหมือนถูกออกแบบมาเพื่อฝังในโปรแกรมขนาดใหญ่ มากกว่าจะเป็นเครื่องมือสำหรับบันทึกข้อมูลได้ง่าย ๆ แบบในบทความนี้
บางที Prolog อาจเป็นเครื่องมือที่ควรใช้จริงก็ได้
Prolog เป็น superset ของ Datalog ดังนั้นทุกอย่างที่ Datalog ทำได้ Prolog ก็ทำได้ทั้งหมดและมากกว่านั้นด้วย แต่บางครั้งสิ่งที่ “มากกว่านั้น” ก็เป็นปัญหา เพราะมันกลายเป็นภาษาแบบทัวริงสมบูรณ์แล้ว จึงอาจไม่จบการทำงาน การให้เหตุผลก็อาจยากขึ้นเล็กน้อย และยังอาจถูกใช้ในทางที่ไม่ปลอดภัยได้
ส่วน Datalog นั้นด้วยความที่มีข้อจำกัด จึงใช้ทางลัดบางอย่างได้ ทำให้หลายกรณีประสิทธิภาพดีกว่า
สุดท้าย ใน Prolog ข้อมูลกับโค้ดเป็นสิ่งเดียวกัน หรือก็คือมี homoiconicity ดังนั้นงาน create/modify/delete จึงแทบจะเท่ากับการแก้ซอร์สโค้ด ถึงจะทำแบบ dynamic ได้เหมือนกัน แต่ก็อย่าคาดหวังว่าการไหลลื่นจะดีมาก และก็ยังต้องซิงก์กันด้วยมืออยู่พอสมควรระหว่างไฟล์กับโปรแกรมที่โหลดไว้
ผมอยากเรียน Prolog ให้ลึกกว่านี้แล้วเอาไปใช้กับ การ query ชุดคำสั่ง
แต่การจำลองตรรกะที่มีทั้งปริมาณและจำนวนเต็มในระดับบิตรวมอยู่ด้วยนั้นค่อนข้างยากทีเดียว