(toiminnot)

hwechtla-tl: Lelut ja vakavasti otettavat

Kierre.png

Mikä on WikiWiki?
nettipäiväkirja
koko wiki (etsi)
viime muutokset


Muistan, että joskus pienenä pirpanana, kun harjoittelin ohjelmoimaan, pidin tiettyjä kieliä "leluina" (Basic, Logo) ja toisia jotenkin "ammattimaisina" tai "asiallisina" (C, C++). (Myöhemmin tein näistä ja muista ennakkoluuloista näyttelyn, näyttely: ohjelmointikielten kuvat.) Samanlaisia jaotteluita tulee vastaan muissakin työvälineissä: Centos on "vakavasti otettava" Linux-jakelu kun Ubuntu on "lelu", Oracle "todellinen" tietokanta, PostgreSQL "lelu" (tai MySQL "oikea", SQLite "lelu", riippuen keneltä kysyy). Jossain vaiheessa CORBA oli vakavasti otettava teknologia ja HTTP-kyselyiden tekeminen suoraan taustapalveluihin epäilyttävää h4xxorointia, vaikka sittemmin tämä jälkemmäinen onneksi lanseerattiin uudella nimellä (REST), ja sitten se onkin ollut katu-uskottavaa. Jos vain ihmiset suostuisivat vielä uskomaan, että skeemallinen JSON ei ole yhtään sen ammattimaisempaa kuin skeematon.

Joka tapauksessa, näissä termeissä on yksi vika: ne eivät tarkoita mitään. Joissain tapauksissa termien taustalla on jotain muuta, joka oikeasti tarkoittaakin jotain. Esimerkiksi Scratch (ohjelmointikieli) ei ole ihan vakavasti otettava, mutta ei sen takia, että se on suunnattu lapsille ja helppo käyttää - vaan sen takia, että esim. käytännön tasolla siinä ei voi käyttää yli 320x200 pikselin näyttötiloja, tai teoreettisemmalla tasolla siinä ei voi parametrisoida aliohjelmakutsuja eikä jakaa koodia useamman hahmon välillä. Sen sijaan Logo (ohjelmointikieli) on suunnattu lapsille ja helppo käyttää, mutta sen "leluus" on ihan sen maineessa, eikä sillä ole mitään todellista pohjaa. Scratchkin on lelu vain, jos sattuu tarvitsemaan sitä, mitä Scratch ei tarjoa; muuten se on ihan mahtava pelien prototyyppausalusta. Totesin esimerkiksi, että kun lapseni haluaa tehdä tietokone-RPG:n, hyvä toimintatapa on se, että lapseni määrittelee Scratch-prototyypilla miten jonkin asian pelissä pitäisi toimia, ja sitten sitä koodataan Javascriptillä.

"Ammattimaisella" tai "vakavasti otettavalla" tarkoitin pienenä sitä, että kyseistä kieltä käytetään isojen, vakavasti otettavien ohjelmien koodaamiseen. Vasta myöhemmin ymmärsin, että näiden ohjelmien ei tarvitsisi olla isoja - niiden isous (ja tavallaan vakavuuskin, eli vaikea ylläpidettävyys) on käytettyjen teknologioiden seuraus, ei syy siihen, miksi noita kieliä halutaan käyttää. Vielä opiskeluaikana ostin ajatuksen, että jokaisen ohjelmoijan pitää oppia tekemään satojen tuhansien koodirivien ohjelmia. Viitisen vuotta myöhemmin väittelin jo siitä, ettei koko konseptissa ole mitään järkeä (http://wiki.c2.com/?FactoringLargePrograms). Nykyään olen sitä mieltä, että jokaisen ohjelmoijan pitää oppia, miten se vältetään. (Yegge sanoo saman asian oikein hyvin täällä: http://steve-yegge.blogspot.com/2007/12/codes-worst-enemy.html)

Minä kasvoin maailmaan, jossa suorituskyvyllä on väliä. Siinä maailmassa jokin väline (kuten Basic) saattoi olla "lelu" ihan siksi, että sen nopeus ei riittänyt piirtämään koko näytöllistä kamaa lähellekään animaationopeutta ja siksi sillä ei voinut tehdä näytön vieritystä. Katsoin kateellisena joitain 2d-lentopelejä Apple ][ c -koneellani ja mietin, että täytyy opetella konekieltä. Myöhemmin ajattelin, että Unixissa pitää suorituskyvyn vuoksi koodata systeemikomponentit C-kielellä ja tajusin/opin vasta myöhemmin, että melkein jokaisen prosessin pullonkaula on verkko- tai levytiedonsiirto ja että taustadaemonin voi tehdä vaikka sh-komentotulkilla, jos huvittaa. Monet "lelukielet" lakkasivat olemasta leluja, koska koneiden suorituskyky kasvoi, mutta monet eivät koskaan leluja olleetkaan, muuten kuin ihmisten asenteissa.

Mutta Javan myötä (ks. Javan huonoista puolista) tajusin, että usein tämmöiset "vakavasti otettavan" maineessa olevat työkalut eivät ole edes nopeampia kuin kovemman abstraktiotason vastineensa. Toki staattinen, tavukoodiksi käännetty (tai natiivikoodiksi JIT-käännetty) kieli on periaatteessa nopeampi kuin joku Lisp-toteutus, joka evaluoi listoja. Periaatteessa. Mutta sitten kun siihen kasataan päälle kaikki ne huonot design-ratkaisut, joita tuon heikon kielen kanssa pärjäämiseen on vaadittu, ja kaikki surkeat design-ratkaisut, joita Java-kehittäjät ovat tehneet ihan yleisen ammattimaisuuden nimissä... niin saadaan systeemi, jossa ohjelmat vievät tolkuttomasti muistia, jaettuja kirjastoja ei ole olemassa, käytetään vain yhtä kieltä sen sijaan että tehtäisiin kukin osa ohjelmasta tarpeisiin parhaiten vastaavalla kielellä, ympäristön käynnistymisaika on ihan himmeä, ja niin edelleen... Kun näin Leiningenin ensimmäistä kertaa, ajatukseni oli: oho, Lispistä on taas saatu yhtä hidas kuin 90-luvun alussa. Toki oli, koska tämä Lisp toimii Java-virtuaalikoneen päällä.

Skriptikielet olisivat periaatteessa hitaampia, koska niiden tulkkaus on hidasta. Mutta tosiasiassa niillä kirjoitetut ohjelmat käyttävät mahdollisimman paljon kielen sisäänrakennettuja ominaisuuksia, jotka on toteutettu hyvin. Javassa, C:ssä ja C++:ssa ei ole kielen suosimia helppokäyttöisiä tietorakenteita. C:ssä tästä seuraa se, että helppokäyttöisiä tietorakenteita ei vain käytetä; C++:ssa se, että ne on laitettu vakiokirjastoon ja generoivat helvetisti koodia staattisen tyypityksen vuoksi; mutta Javassa se tarkoittaa, että niistä on vakiokirjastoissa monia erilaisia versioita ja Java-ohjelmat kuluttavat suuren osan ajastaan viemällä tietoa yhdestä tietorakenteesta toiseen.

"Ammattimainen" siis ei tarkoita ainakaan hyvää. "Ammattimainen" tarkoittaa sitä, että ammattilaiset käyttävät sitä ollakseen tehokkaasti tuottamatta tulosta. "Lelu" voi tarkoittaa jotain järkevää tai olla tarkoittamatta, mutta jos kyse ei ole mistään järkevästä, se yleensä tarkoittaa juuri "helppo" ja "lapsille suunnattu" (kuten Scratch). Nyt nämä kaksi ominaisuutta - helppous, ja se että jokin on suunnattu lapsille - sattuvat olemaan ne kaksi ominaisuutta, jotka lisäävät kenen tahansa ohjelmoijan tuottavuutta. Jos esimerkiksi haluan saada viivan ruudulle, kumpi on parempi rajapinta käyttäjän näkökulmasta?

Logo:

fd 100

C++ ja Qt (http://stackoverflow.com/questions/24672146/qpainter-draw-line):

#include <QApplication>
#include <QLabel>
#include <QPicture>
#include <QPainter>

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QLabel l;
   QPicture pi;
   QPainter p(&pi);

   p.setRenderHint(QPainter::Antialiasing);
   p.setPen(QPen(Qt::black, 1));
   p.drawLine(100, 100, 100, 0);
   p.end();

   l.setPicture(pi);
   l.show();
   return a.exec();
}

Oikeasti, kaikki kirjastot ja rajapinnat pitäisi suunnitella lapsille. Niiden pitäisi olla niin helppoja käyttää, että käyttäjän koodissa tarvitsee sanoa vain se, mitä oikeasti tarkoittaa. Yllä olevassa esimerkissä Qt-ratkaisu on tietysti tavallaan "geneerisempi", mutta se johtuu vain siitä, että Logossa on järkevät oletusasetukset.

Sama homma tulee vastaan vaikkapa tietorakenteissa. Haluan luoda tietoelementin, jossa säilötään tilitapahtumalista. Kumpi on parempi rajapinta käyttäjän näkökulmasta?

Python:

response = dict(num_total=num_total, num_returned=len(transactions),
                transactions=transactions)

Java:

import fi.xxx.meta.api.campaign_details.v0_1.response.ObjectFactory;
import fi.xxx.meta.api.campaign_details.v0_1.response.ResponseType;
import fi.xxx.meta.api.campaign_details.v0_1.response.TransactionType;

import javax.xml.bind.JAXBElement;
import java.util.List;

public class DetailResponseUtils {
    private static ObjectFactory factory = new ObjectFactory();

    public static JAXBElement<ResponseType>
            createResponseType(List<TransactionType> transactions,
                            int allTransactionsSize) {
        ResponseType responseType = factory.createResponseType();
        responseType.setNumberOfTransactionsTotal(allTransactionsSize);
        responseType.setNumberOfTransactionsReturned(transactions.size());
        responseType.getTransaction().addAll(transactions);
        return factory.createCampaignDetailsResponse(responseType);
    }
}

Huomaa, että tämän käsin kirjoitetun koodin lisäksi JAXB on generoinut ObjectFactory-, ResponseType- ja TransactionType-luokat, jotka kaikki kasvattavat ajossa olevan ohjelman kokoa.

Eli, jos "lelu" tarkoittaa "helppo" ja "suunnattu lapsille", niin se tarkoittaa hyvää. Mutta en toki yritä väittää, että olisi helppoa sanoa, mikä on helppoa. Eikä kaikki lapsille suunnattu oikeasti sovellu lapsille (esimerkiksi: Tom&Jerry, Bratz). Ja joskus pitää olla kokemusta, millaisiin ongelmiin voi törmätä, ennen kuin ymmärtää, miksi jokin rajoite on ongelma; lapseni tajuavat Scratchin ongelmat sitten, kun haluaisivat tehdä saman muutoksen 5 eri vihollisen toimintaan, mutteivät enää jaksa copypasteta skriptiä hahmojen välillä ja sitten tehdä siihen käsin vihollisesta riippuvia muutoksia.

Mutta tämä kirjoitus yrittää kertoa teille kokemuksen syvällä rintaäänellä, miksi "ammattimaisuus" ja "vakavasti otettavuus" on ongelma. Sillä usein on niin, että ammattilaiset ratkaisevat vaikeita ongelmia, mutta ne ongelmat vain eivät olisi vaikeita, elleivät ammattilaiset käyttäisi ammattimaisia työkaluja, negatiivisen nettoarvon ohjelmistoja. (Sama ajatus, eri sanat: https://thedailywtf.com/articles/the-enterprise-backup-batch)


kommentoi (viimeksi muutettu 17.07.2019 03:26)