La Trankvila programlingvo

✍️ Skribite je la 2024-07-05 per 2031 vortoj.
Parto de languages Esperanto cs programming-languages

Enkonduko

Rikardo estas amiko en mia Esperanta komunumo. Li menciis la programlingvon Trankvila antaŭ proksime du jaroj. Trankvila estas programlingvo kaj havas retejon. Rikardo prelegis pri Trinkvila [ĉe KAEST 2018 (bedaŭrinde la lumbildoj ne estas legeblaj sed enhaveblaj en la retejo). Lia prelego ĉe Grazer Linuxtage 2015 ne havas registron.

Nu, mi volis lerni pri la programlingvo ĉar mi estas programisto kaj jam konas diversajn programlingvojn. Mi prelegas ĉiumonate pri ŝatokupaj temoj en Esperanto komunumo kaj hieraŭ mi prelegis[1] pri Trankvila. Nun sekvas enkondukon al Trankvila.

Por komenci

Trankvila estas …

  • pura programlingvo. Oni difinas variablojn kaj la ordo de la plenumo ne gravas.

  • Do ankaŭ oni ne povas legi & skribi dosierojn. Oni ne povas legi la aktualan tempon de la operaciumo. Do ne estas praktika programlingvo krom kalkulilo.

  • Laŭ mi kaj laŭ la koncepto de programparadigmoj, la programlingvo ĉefe estas funkciema sed Rikardo havis argumenton ke Trankvila ankaŭ havas objektemajn elementojn.

  • Trankvila havas koncepton de rekordon sed ne alternativon (kompare al union en C). Do Trankvila ne havas algebrajn datumtipojn.

  • Trankvila ebligas vin elporti la signaturojn de difintaj elementoj de unu modulo.

  • La unua realigo uzas la Java virtualan maŝinon.

  • Laŭ rimarko de Rikardo dum mia prelego la nuna realigo havas versionon 0.1 kaj la programlingvo ne ŝanĝis ekde 2015.

  • Trankvila estas malfermfonta kodo pere de GPL.

Se ni komencas, ni bezonas operaciumon kiu subtenas plenumi Java programojn. Do en mia maŝino, mi instalis OpenJDK kaj nun mi povas uzi java en la terminalo. Poste mi elŝultis la Trankvila Modules kaj malkompaktigis la ZIP dosieron.

TRANKVILA MODULE SalutonMondo BY Luko;
LET
  saluto* := "Saluton mondo!" + new line;
END.

Mi enmetas ĉi tiun kodon en dosiero saluto.trankvila[2]. En la terminalo mi povas voki

java -jar "Trankvila Modules/trankvila.jar" --file saluto.trankvila --verbose --evaluate saluto

La rezulto estas …

compiled module SalutonMondo (v1.0) BY Luko (0 ms)
Saluton mondo!

(strict evaluation: 0.0 seconds; total: 0.0 seconds; result type: List [Character])

Pli malfacila programo

TRANKVILA MODULE Realigo de foldl BY Luko;
LET
        -- difino
        Binary operation [T] = FUNCTION (a, b: T): T;
        foldl* [T] (op: Binary operation [T]; base: T; rest: List [T]): T :=
                IF rest: length = 0 THEN
                        base
                ELSE IF rest: length = 1 THEN
                        op(rest: get (0), base)
                ELSE
                        op(rest: get (0), foldl [T] (op, base, rest: drop (1)));

        -- realigo
        min: Binary operation [Integer] := ?(a, b) IF a < b THEN a ELSE b;

        testcase1* := foldl [Integer] (min, 42, LIST [Integer] ());
        testcase2* := foldl [Integer] (min, 42, LIST [Integer] (42));
        testcase3* := foldl [Integer] (min, 55, LIST [Integer] (42));
        testcase4* := foldl [Integer] (min, 42, LIST [Integer] (55));
        testcase5* := foldl [Integer] (min, 77, LIST [Integer] (66, 42));
        testcase6* := foldl [Integer] (min, 42, LIST [Integer] (77, 55, 66));
        testcase7* := foldl [Integer] (min, 77, LIST [Integer] (66, 42, 88));

        my program output* := foldl [Integer] (min, 4, 3 -> LIST [Integer] (5));
END.

Mi malsukcesis realigi la tipon std::result::Result de rust. Finfine mi sukcesis verki programon kiu kalkulas la rezulton de la foldl operatoro de Haskell.

  1. Unue mi difinas tipon por funkcio kiu prenas du argumentoj kaj redonas valoron de la sama tipo. La funkcia tipo nomas Binary operation. T estas tipargumento. Poste, la difinon de foldl sekvas.

  2. La linio kun Binary operation uzas = por difini tipon. Sed en la linio de foldl* ni uzas := por difini konduton.

  3. rest: length estas voko de metodo length de la argumento rest. Ni komparas entjerojn per =.

  4. rest: get (0) estas voko de metodo get kun argument 0. La unua elemento de vico havas indekso 0.

  5. En ĉiu loko de foldl, vi bezonas la tipargumenton.

  6. min estas realigo de Binary operation kun tipargumento Integer. La difino uzas sennoman funkcion kun du argumentoj a kaj b. La difino de la sennoma funkcio estas triviala kaj nur uzas unu IF blokon.

  7. Finfine, mi uzas diversaj difinojn por testi la programon. La rezulto de ĉiu testkazo estas 42.

  8. Komprenu ke LIST [Integer] () kreas vicon de entjeroj. Komprenu ke 3 → LIST [Integer] (5) ŝovas la elementon 3 al la vico kun elemento 5. Do 3 → LIST [Integer] (5) egalas LIST [Integer] (3, 5).

Mia opinio

  1. Mi ŝatas ke Trankvila uzas UTF-8 dosierojn.

  2. Mi ŝatas ke Trankvila uzas tabon por krommarĝeni.

  3. La sintakso plejparte preferas ŝlosilvortojn antaŭ ol interpunkcio. Mi preferas ŝlosilvortojn.

  4. La uzado de blankaspaco en variablaj nomoj estas malkutimaj kaj hodiaŭ mi ne certas ĉu estas bona/malbona ideo. Certe mi ne povas legi kodon tiel rapide, ĉar mi alkutimigis pro ĉiu alia programlingvo (kontraŭ FORTRAN, mi lernis).

  5. Mi ŝatas ke Trankvilaj programoj komencas per la vorto trankvila. Se oni ne konas la programlingvon, oni povas serĉi Trankvila kaj trovas la informon.

  6. Mi ŝatas la elporteblon de moduloj.

  7. Certe estas malrapide dum plenumo (mi ne mezuris). Signoĉenoj estas vicoj de signoj. Ĉar Trankvila uzas UTF-8 dosierojn ĉiu signoĉeno estas transformata al UTF-16 ĉar Java uzas UTF-16 interne. Mi ne certas pri detaloj sed Trankvila konsideras signoĉenojn kiel vico de signo. Unu signo ĉiam bezonas kvar bitokojn. Do signoĉenoj bezonas multe da spaco do multe da tempo por transformi. Finfine estas teknika detalo sed bona rapida realigo ne ekzistas.

  8. Se oni prenas valoron de malplena Option valoro (do oni vokas get de Option), Trankvila vokas netraktebla eraro. Tio tute ne taŭgas por mi. La difino estas videbla en la dosiero Standard.trankvila je la linio 193:

    (:Option) get* : T := ERROR ("optional value not provided");
  9. Trankvila ne havas tipinferencon. Do vi devas skribi multe da kodo por tipoj kiu ofte tute ne helpas kompreni.

  10. List* [T] = RECORD …​ END; Nonempty List* = ABSTRACT RECORD (List) END;. Do List estas rekordo kun unu tipargument T. Nonempty List estas abstrakta rekordo kiu heredadas de List per (List). Ĉu Nonempty List bezonas tipargumenton? La respondo estas jes, sed la difino ne montras tion. Mi tute ne scias kion okazos ke vi havas du tipargumentoj T en gepatraj niveloj. Ĝenerale mi preferas ke la tuta vero estas videbla en la difino.

  11. Mi supozas ke ekzistas problemojn pri la distingo de voko sen argumentoj (p.e. rest: length) kaj montrilo al metodo. PHP havis la saman problemon, ĉar ili realigis ident kiel ident() kaj havis problemojn post kiam ili konservis funkciojn en variabloj.

  12. La verkisto klopodis pri la sintakso kaj bonaj erarmesaĝoj (p.e. “character combination "==" does not lead to a valid token; use a single "=" to check for equality”). Sed semantikaj elementoj kaj biblioteko de funkcioj mankas al mi. Se mi verkas ĉi tiun programlingvon mi (p.e.) enkondukus mapigan datumstrukturon.

Rekomendoj por progresi

  1. Ĉiu versiono de programoj devas havi versionan numeron; ankaŭ Trankvila.

  2. Ŝanĝu la tiparon en la retejo al egallarĝa tiparo. Ja estas stranga alkumitiĝo, sed nun mi kredas ke la interpunkciaj kaj spacaj signoj estas pli legebla tiel.

  3. Subtenu sintaksan emfazon por ekzemploj en la retejo.

  4. Movu la EBNF sintakson suben en la retejo. EBNF sintakso estas malpli grava ol klarigi la paŝojn por atingi rezulton de “Saluton mondo” programo. Komparu al aliaj programlingvaj retejoj.

  5. La interaktiva reĝimo ne distingas inter enteksto kaj elteksto. Mi preferus alian aliron (p.e. pitono uzas >>>).

  6. “Column numbers are always calculated according to a tab width of 8 characters”. Mi ne komprenas ĉi tiun frazon. Tabo estas unu signo.

  7. La signifon de rondaj krampoj (p.e. Nonempty List [T] = ABSTRACT RECORD (List) END;) ne estas klarigita. Rikardo reuzis tion el Oberon-2. Rondaj krampoj signifas baza tipon (similas heredadon al objektemaj programlingvoj).

  8. Oni bezonas alian rimedon por voki funkcion. Nun oni ne povas voki (p.e.) funkcion kun argumentoj per --evaluate sed nur en la interaktiva reĝimo. Kiel oni testas funkciojn aŭtomate? Eble oni volas distingi inter moduloj por difini kaj moduloj por voki. Ĝenerale ni bezonas ilojn.

  9. La difino de “Partitioned Lists” konfuzis min. Mi ne komprenis la intencon. Oni bezonas klarigon kiel ĉi tiu tipo estas baza elemento de programlingvo.

  10. La signifon de la signo ^ ne estas klarigita en la retejo. Nur okazas en la EBNF dokumento.

  11. En Standard.trankvila, la nomo de la tipo estas LInked List. Kiel la dua signo estas majuskla?

  12. Mi uzas elementojn de rekordo. Sed la iloj ne estas klarigitaj. Kiel oni konstruas objecton de rekordo? Kio estas la valoro se kampo de rekordo ne havas deklaritan valoron? Kiel mi ne povas krei ĉi tiun defaŭltan valoron? Kiel mi elmetas kampa valoro de rekordo. Oni bezonas pli da klarigoj. Finfine mi nur trovis Point 3d (x! 1.0, y! 2.0, z! 3.0) en ekzemplo kaj taksis la uzadon.

  13. Mi tute ne komprenis kiel oni povas testi ĉu la tipo Provided havas valoron aŭ ne. Mi aĉe komparis la rezulton de out kun "". La retejo ne klarigas sed la dosiero uzas la operatoro IS por distingi. Mi finfine lernis tro malfrue.

  14. Mi ne komprenas kial List ne havas metodojn first kaj rest. Ĝeneral Trankvila havas Option tipon, do first devus redoni Option [T] kiel rezulto. Se vi distingas inter ne-malplenaj tipoj kaj malplenaj tipoj, la tipsistemo ne estas uzebla. Vi ne povas verki rekursivaj programoj ĉar (p.e.) Array List redonas List per metodo rest. Kial ni subite ne povas progresi kun la tipo Array List en rekursiva strukturo?

  15. Mi ne komprenas kial oni ne povas uzi la difinon list* [T] (…​: T): Array List [T]; ekster Standard.trankvila. list (3) ne funkcias sed LIST [Integer] (5) pravas.

  16. Trankvila havas kutiman problemon de statikaj tipsistemoj: se mi tute ne interesas min pri la tipo de argumento ĉar mi ĉiam (p.e.) redonas "" - kial mi devas deklari la argumenttipojn?

  17. Trankvila devus difini la precizecon de la datumtipoj. En la Java realigo, Trankvila uzas 64 bitojn. Laŭ mi tio estas aĉa afero ĉar la programisto ne scias kiam li devas trakti kromaĵojn kaj erarojn. En la komenco de la programlingvo C ankaŭ ekzistis la saman ideon, sed hodiaŭ neniu skribas programojn per la datumtipo int. Programistoj ĉiam uzas int32_t de la biblioteko stdint.h. La modernaj programlingvoj neniam uzas ĉi tiun principon. rust havas abstraktigo por ne-precizaj entjeroj nome std::num. Sed oni ĉiam povas demandi la precizecon de aparta valoro se oni uzas ĉi tiun abstraktigon.

Konkludo

Trankvila estas pura funkciema programlingvo. Semantikaj elementoj, pli da ekzemploj, kaj biblioteko mankas al mi por uzi la lingvon praktike. Sed mi ĉiam ĝojas provi novan programlingvon. La forto de Trankvila laŭ mi estas videbla en la sintakso kaj la realigo per Java Runtime Environment (JRE) estas ĝenerala. Se mi ne sufiĉe komprenis detailojn, mi studis la Java kodon. Tio estas la forto de malfermfonta kodo.

Dankon al Rikardo!


1. Honeste: mi ne havis multe da enhavo kaj ni diskutis plejparte.
2. Por krommarĝeno, oni uzas horizontalan tabon en Trankvila.