Skip to content
Ridgun
English

Ridgun

The language itself: a compiler that compiles itself, the stable project tool, the specification and the book

06-datu-mota-aljebraikoak-eta-polimorfismoa.md227 lines · 13.2 KBRaw
1# 06. kapitulua: Datu-Mota Aljebraikoak (ADTak), Polimorfismoa eta zergatik hemen ez dugun `null` edo `Any` nahi
2
31965ean, Tony Hoarek erreferentzia nulua (`null` / `nil`) asmatu zuen. Berrogei urte geroago barkamena eskatu zuen jendaurrean eta *"nire mila milioi dolarreko akatsa"* deitu zion.
4
5Zergatik? `null` duten lengoaietan, funtzio batek bere sinaduran hitzematen dizulako `Erabiltzailea` bat itzuliko dizula... eta gutxien espero duzunean `null` itzultzen dizulako eta goizeko hiruretan zerbitzaria lurrera botatzen dizulako.
6
7Eta beste horrenbeste gertatzen da objektuen herentziarekin eta `Any` (edo `Object`) motarekin: zenbaki bat eta testu bat `[1, "bi"]` zerrenda batean sartzen dituzu, lengoaiak sorbaldak jasotzen ditu eta esaten du *"ados, ba orain hau `List[Any]` bat da, zorte on barruan zer dagoen asmatzen zeharkatzen duzunean"*.
8
9Ridgunen bi arazo horiek errotik mozten ditugu **Datu-Mota Aljebraikoekin (ADTak)** eta **Polimorfismo Parametrikoarekin**. Ikasgai gogorreko izena duela dirudi, baina bost minutuan ikusiko duzu datuak modelatzeko munduko modurik naturalena dela.
10
11---
12
13## 6.1. Biderkadura-Motak (Erregistroak): Betiko zure `struct`-ak, baina superbotereekin
14
15Entitate berari dagozkion hainbat datu elkartu nahi dituzunean (adibidez, zerbitzari bat bere izenarekin, bere IParekin eta bere atakarekin), **`type`** bat definitzen duzu bere eremuekin:
16
17```ridgun
18type Zerbitzaria = ostalaria: String, ataka: Int, tls_aktibo: Bool
19```
20
21Lerro hori idazte hutsarekin, Ridgunek **lau gauza** oparitzen dizkizu automatikoki:
22
23### 1. `Zerbitzaria` eraikitzailea
24```ridgun
25nire nodoa => Zerbitzaria
26nire nodoa = Zerbitzaria "api.adibidea.eus" 443 True
27```
28
29### 2. *Getter* funtzio puru bat eremu bakoitzerako (`ostalaria`, `ataka`, `tls_aktibo`)
30Ez duzu `s.getOstalaria()` edo antzekorik idatzi behar; eremu-izen bakoitza zuzenean datu hori ateratzen duen funtzio bat da:
31
32```ridgun
33konexio urla Zerbitzaria => String
34konexio urla zrb = "Konektatu hona: " + (ostalaria zrb) + " {ataka zrb} atakan"
35```
36
37### 3. Erregistroen eguneratze funtzionala (`with eremua: balioa`)
38Imajinatu 12 edo 15 eremuko konfigurazio-erregistro bat (`ls` komando baten edo web-zerbitzari baten aukerak bezala) eta eremu bat edo bi bakarrik aldatu nahi dituzula gainerakoak ukitu gabe. Erregistroa 15 eremuak eskuz pasatuz berreraiki beharrean, **`with`** eta bi puntu **`:`** erabiltzen dituzu (`type`-aren definizioan bezalaxe):
39
40```ridgun
41aktibatu garapen modua Zerbitzaria => Zerbitzaria
42aktibatu garapen modua zrb = zrb with ataka: 8080, tls_aktibo: False
43```
44
45Bi munduetako onena:
46- Zure kodean **% 100ean aldaezina eta segurua** da (konpiladoreak egiaztatzen du `ataka` eta `tls_aktibo` `Zerbitzaria`-n existitzen direla eta mota zuzena dutela).
47- Azpitik, `zrb` beste inorekin partekatuta ez badago, exekutagarri natiboak bere memoria *in-place* berrerabiltzen du (`Arc::try_unwrap`) gainerako eremuak kopiatu gabe.
48
49### 4. *Pattern matching* osoa erregistroa erditik ireki nahi baduzu
50```ridgun
51ataka segurua da Zerbitzaria => Bool
52ataka segurua da (Zerbitzaria _ 443 True) = True
53ataka segurua da _ = False
54```
55
56---
57
58## 6.2. Batuketa-Motak (`|` bidezko aldaerak): Nola erretiratu `null`, salbuespenak eta herentzia
59
60Orain dator onena.
61Erregistro arrunt batek `ostalaria` **ETA** `ataka` **ETA** `tls_aktibo` ditu.
62Baina mundu errealean askotan hau esan behar duzu: *"Ordainketa-metodo bat **EDO** bere zenbakia duen txartel bat da, **EDO** bere IBANa duen transferentzia bat da, **EDO** eskudirua da (datu gehigarririk ez daramana)"*.
63
64Objektuetara bideratutako programazioan `OrdainketaMetodoa` interfaze bat, hiru azpiklase hiru fitxategi desberdinetan, eraikitzaileak, `instanceof`... sortu beharko zenituzke. Ridgunen **lerro bakarra** da:
65
66```ridgun
67type OrdainketaMetodoa = Txartela String Int | Transferentzia String | Eskudirua
68```
69
70Eta begira zer gertatzen den funtzio batean `OrdainketaMetodoa` bat prozesatzen duzunean:
71
72```ridgun
73deskribatu ordainketa OrdainketaMetodoa => String
74deskribatu ordainketa (Txartela zenb _) = "{zenb}-rekin amaitutako txartel bidezko ordainketa"
75deskribatu ordainketa (Transferentzia ibana) = "Transferentzia {ibana} kontutik"
76deskribatu ordainketa Eskudirua = "Eskudirutan ordaintzea leihatilan"
77```
78
79Aldaera bakoitzak (`Txartela`, `Transferentzia`, `Eskudirua`) berarentzat zentzua duten datuak soilik daramatza itsatsita. **Ezinezkoa** da hutsez `ibana` bat zintzilik duen `Eskudirua` ordainketa bat eraikitzea.
80
81---
82
83## 6.3. "Eta nola sartzen dut `[1, "bi"]` zerrenda batean `Any` erabili gabe?"
84
85Itzul gaitezen milioiaren galderara. Imajinatu CSV edo JSON fitxategi bat irakurtzen ari zarela, non gelaxka bat zenbaki osoko bat, testu bat edo hutsa izan daitekeen.
86
87`Any` mota bat bazenu, `[1, "bi"]` sartuko zenuke eta konpiladorearen laguntza guztia galduko zenuke: une horretatik aurrera zerrendaren gaineko edozein eragiketa begiak estalita ibiltzea litzateke.
88
89Ridgunen (Haskell edo Rusten bezalaxe), bi segundotan batuketa-mota bat sortzen duzu, zerrenda horren barruan zein gauza bizi daitezkeen zehatz-mehatz azaltzen duena:
90
91```ridgun
92type GelaxkaBalioa = Osokoa Int | Testua String | Hutsa
93
94formateatu gelaxka GelaxkaBalioa => String
95formateatu gelaxka (Osokoa n) = "Zenbakia: {n}"
96formateatu gelaxka (Testua s) = "Testua: {s}"
97formateatu gelaxka Hutsa = "(hutsik)"
98
99nire errenkada => [GelaxkaBalioa]
100nire errenkada = [Osokoa 1, Testua "bi", Hutsa]
101```
102
103Ikusten duzu aldea?
104- `Any`-rekin, konpiladoreak ez daki zerrendan osokoak, kateak ala USB bidez konektatutako kafe-makina bat dauden.
105- `[GelaxkaBalioa]`-rekin, zenbakiak eta testuak zerrenda berean nahas ditzakezu, **baina konpiladoreak % 100ean daki hiru aukera horiek baino ez direla existitzen** eta kasu bakoitza (`Osokoa n`, `Testua s`, `Hutsa`) erabili aurretik despaketatzera behartzen zaitu. Segurtasun osoa malgutasun izpi bat ere galdu gabe.
106
107---
108
109## 6.4. `Option` eta `Result`: Zerbait ez egon daitekeenean edo huts egin dezakeenean
110
111Ridgunen `null` existitzen ez denez eta airean lehertzen diren salbuespenik (`throw new Exception()`) existitzen ez denez, lengoaiak bi batuketa-mota unibertsal integratuta dakartza:
112
1131. **`Option`**: edo **`Some balioa`** duzu, edo **`None`** (ezer ez) duzu.
1142. **`Result`**: edo eragiketa ondo atera da (**`Ok balioa`**), edo arrazoi zehatz bategatik huts egin du (**`Err errorea`**).
115
116```ridgun
117bilatu zatiketa Int Int => Result
118bilatu zatiketa _ 0 = Err "Ei! Ezin da zeroz zatitu"
119bilatu zatiketa a b = Ok (a / b)
120
121inprimatu em Result => ()
122inprimatu em (Ok n) = echo "Emaitza {n} da"
123inprimatu em (Err msg) = echo "Hutsegite kontrolatua: {msg}"
124```
125
126Inork ezin du errore bat deskuiduz alde batera utzi, `Result` motak berak barruan `Ok` bat ala `Err` bat dagoen begiratzera behartzen zaituelako.
127
128---
129
130## 6.5. Polimorfismo Parametrikoa: Funtzio generikoak `A`, `B`, `T`...-rekin
131
132Demagun `(x, y)` bikote bat alderantzikatzen duen edo zerrenda bateko lehen elementua ateratzen duen funtzio bat idazten duzula. Benetan axola du zerrenda `Int`, `String` ala `Zerbitzaria` motakoa den? Inola ere ez! Algoritmoa berdin-berdina da.
133
134Ridgunen, mota-sinadura bateko **edozein letra larri banakok (`A`-tik `Z`-ra)** automatikoki **komodin-mota polimorfiko (mota-aldagai)** gisa funtzionatzen du:
135
136```ridgun
137buru segurua [A] A => A
138buru segurua [] lehenetsia = lehenetsia
139buru segurua (x:_) _ = x
140
141biratu bikotea (A, B) => (B, A)
142biratu bikotea (x, y) = (y, x)
143```
144
145Erreparatu `buru segurua [A] A => A`-ren magiari:
146- Ez da kontrolik gabeko `Any` bat: **`[A]` zerrendaren motak, `lehenetsia` balioaren motak (`A`) eta itzulitako motak (`A`) bera izan behar dutela** dio.
147- `buru segurua [10, 20] 0` deitzen baduzu, konpiladoreak hariak lotzen ditu (`A = Int`) eta `Int` bat itzultzen dizu.
148- `buru segurua ["a", "b"] "bat_ere_ez"` deitzen baduzu, hariak lotzen ditu (`A = String`) eta `String` bat itzultzen dizu.
149- Baina tranpa egiten saiatzen bazara eta `buru segurua [10, 20] "bat_ere_ez"` deitzen baduzu, zas! Konpiladoreak lehorrean gelditzen zaitu, lehen argumentuak `A` `Int` dela dioelako eta bigarrenak `A` `String` dela.
150
151---
152
153## 6.6. Sortu zure eragile propioak (`op ::`): Bektoreetatik neurrira egindako esaldietara
154
155Zergatik zenbaki natiboek bakarrik izan behar zuten `+` edo `*` erabiltzeko eskubidea? Eta zergatik konformatu betiko eragileekin zure arazorako sintaxi ezin hobea asma dezakezunean?
156
157**`op ::`**-rekin (edo zuzenean aurrizkirik gabe eragile-ereduak erabiltzen badituzu) bi trebezia egin ditzakezu:
158
159### 1. `+`, `-`, `*`, `/`, `%` eragileei zure mota propioekin eragiketak egiten irakastea
160```ridgun
161type Konplexua = erreala: Int, irudikaria: Int
162
163op :: Konplexua + Konplexua => Konplexua
164 a + b = Konplexua ((erreala a) + (erreala b)) ((irudikaria a) + (irudikaria b))
165```
166
167### 2. *Mixfix* eragileak sortzea (esaldi natural gisa irakurtzen direnak, `type` eta `with` barne)
168Eragile-sinadura batean, **hasieran letra larria duten hitzak (`Int`, `String`, `A`..`Z`) edo kortxete/parentesi artekoak (`[Int]`, `(A, B)`) argumentuetarako hutsuneak dira**, eta **letra xehez idatzitako hitzak eragilearen esaldiaren parte dira** (baita `type` edo `with` bezalako hitz ohikoak ere, Ridgunen testuingurukoak direnak eta inoiz blokeatzen ez zaituztenak):
169
170```ridgun
171op :: baldin Bool orduan A bestela A => A
172 baldin True orduan v bestela _ = v
173 baldin False orduan _ bestela f = f
174
175op :: format String type String with Int => String
176 format label type kind with w = "{label} ({kind}, width={w})"
177
178main => ()
179main =
180 mezua = baldin (10 > 5) orduan "Hamarrak irabazten du!" bestela "Ezinezkoa"
181 echo mezua
182 echo (format "panela" type "ertzduna" with 80)
183```
184
185Zure eragile hirutar polimorfiko propioa eta formateatzeko DSL bat fabrikatu berri dituzu Ridguneko **3 lerrotan**.
186
187---
188
189## 6.7. Eraikitzaile partekatuak, mota-aldaera zuzenak, `expand type` eta zuriune naturalak `fn ::`-n
190
191Ridgunek datu-mota aljebraikoak eta funtzioak are naturalago eta hedagarriago bihurtzen ditu:
192
1931. **Eraikitzaile-izen partekatuak `type` ezberdinen artean**: Batura-mota ezberdinek eraikitzaile-izen bera (`Error`, `Var`, `Lit`) berrerabil dezakete aritate berarekin edo ezberdinarekin; mota-egiaztatzaileak sinadurako zutabearen arabera bereizten ditu.
1942. **Mota-izen zuzeneko aldaerak errepikapenik gabe (`type Something = String | Int | MyStruct Float Int`)**: Aldaera bat oinarrizko mota bat denean (`String`, `Int`, `Float`/`Real`, `Bool`, `Char`), ez duzu `Int Int` edo `String String` errepikatu behar. Balio zuzenak (`"kaixo"`, `42`) edo eraikitzaile esplizituak (`String "kaixo"`, `Int 42`) pasa ditzakezu, eta ereduak parekatu bai literalekin (`"admin"`, `0`) bai eredu tipatuekin (`(String s)`, `(Int n)`).
1953. **Mota-hedapen irekia (`expand type`) eta `fn ::` klausulen bateratze automatikoa**: `expand type` erabiliz aldaera berriak gehitu diezazkiokezu batura-mota bati edo eremu berriak erregistro bati. Funtzio bererako `fn ::` berriro deklaratzean, klausula berriak automatikoki bateratzen dira edozein `_` komodin-klausularen **aurretik** (`expand fn` idatzi beharrik gabe).
1964. **Zuriune naturalak `" "` `_`-ren ordez `fn ::`-n, funtzio natiboetan eta fitxategi-izenetan**: `op ::`-n bezala, hitz anitzeko `fn ::` izenak, funtzio natiboen deiak (`assert equals`, `str len`, `read file`) eta `.rg` fitxategi-izenak (`test core language.rg`) zuriune naturalekin idatz daitezke:
197
198```ridgun
199type Something = String | Int | MyStruct Float Int
200
201fn :: deskribatu zerbait Something => String
202 deskribatu zerbait "admin" = "admin_berezia"
203 deskribatu zerbait 0 = "zero"
204 deskribatu zerbait (String s) = "testua:{s}"
205 deskribatu zerbait (Int n) = "osoa:{n}"
206 deskribatu zerbait (MyStruct ratio cnt) = "egitura:{ratio}:{cnt}"
207 deskribatu zerbait _ = "bestea"
208
209expand type Something = Bool
210
211fn :: deskribatu zerbait Something => String
212 deskribatu zerbait (Bool b) = "boolearra:{b}"
213```
214
215### 5. Mota generiko propioak (`type Result T E`, `type Page T`) eta moduluen izen-espazioak (`import ... as`, `mod.Mota`)
216Funtzio polimorfikoez gain, **zure batura-mota eta erregistro generiko propioak** deklara ditzakezu mota-parametroekin (`A`..`Z`), baita izen bereko motak, eraikitzaileak, eremuak eta funtzioak modulu ezberdinetan talkarik gabe antolatu ere (`module ::`, `import db as d`, `http.Config`, `d.Config`). Modulu bakoitzak **3 mailako inportazio-ikusgarritasun zorrotza** du: aurrizkirik gabeko izenak lehenik **(1) bere deklarazioetan** bilatzen ditu (`module :: izena` partekatzen duten fitxategi guztiak barne), ondoren **(2) bere `import` zuzenen artean** eta azkenik **(3) inportazio iragankorretan**, inportatu gabeko moduluek edo aplikazio nagusiak liburutegi bat inoiz kutsa ez dezaten:
217
218```ridgun
219type Emaitza T E = Arrakasta T | Hutsegitea E
220type Orrialdea T = elementuak: [T], guztira: Int
221
222fn :: atera arrakasta (Emaitza A String) A => A
223 atera arrakasta (Arrakasta v) _ = v
224 atera arrakasta (Hutsegitea _) d = d
225```
226
227**07. kapituluan** zure prozesadorearen nukleo guztiak aldi berean lanean jarriko ditugu izerdi tanta bat ere bota gabe: **goi-mailako funtzioak (`map`, `||map`, `filter`, `||filter`, `fold`), itxitura lexikoak (`(x => expr)`), `||` grafo paraleloak `->` geziekin eta kanal konkurrenteak**.