Parannuksia Java-kieleen
Heinäkuu 29th, 2006
Tuumailin tässä muutamia juttuja, jotka mielestäni selkeyttäisivät Java-kieltä:
1 a. Omat primitiivityypit BigInteger ja BigDecimal-luokille
Vaikkapa “integer” ja “decimal”. Näin esimerkiksi rahoja tai suuria lukuja laskettaessa säästyttäisiin turhalta visuaaliselta roskalta.
Nyt:
BigDecimal result = new BigDecimal("1.1").multiply(new BigDecimal("3"))
Jälkeen:
decimal result = 1.1 * 3
Nykyselläänhän esimerkiksi 1.1 * 3 tuottaa Javassa (ja monessa muussakin kielessä) tuloksen: 3.3000000000000003
Tässä siis noudatettaisiin “Principle of least surprise“-filosofiaa. Pidemmälle vietynä homma tietysti tehtäisiin oikeaoppisesti eksaktilla numeerisella tornilla:
rational result = 1.0 / 3
String decimalForm = result.toDecimalString(2, RoundingMode.HALF_UP)
assertEquals("0.33", decimalForm)
Tässä tapauksessa decimal vaihtuisi rationaliin ja laskettaessa voitaisiin olla varmoja, ettei tarkkuus laske. Esimerkiksi BigDecimal heittää poikkeuksen ylläolevasta laskutoimituksesta.
Jos muuten kompleksilukuja joutuu laskeskelemaan, niin kannattaa vilkaista JScience-kirjastoa.
1 b. Primitiivityyppien käsittely luokkina
Kieltämättä tämä olisi vähän tällainen “nice to have”-ominaisuus…
Esim:
1.negate()
Nyt kun meillä on autoboxing (eli primitiivimuuttuja vaihtuu tarvittaessa luokka-instanssiksi), niin miksei primitiivimuuttujiakin voisi samantien käsitellä luokkina samoin kuin Stringiä on voinut aina:
"tekstiä".length()
2 a. Muuttujat ja parametrit defaulttina finaleiksi
Tilaansa muuttavat arvot voisi merkitä esimerkiksi uudella “var”-avainsanalla (lyhennys sanasta variable; vaihteleva, epävakainen, muuttuva).
var String phoneNumber
2 b. Null arvo olisi sallittu vain muuttujille, jotka on merkitty kysymysmerkillä
Esim:
var String phoneNumber? = null
// == Nullable<String> phoneNumber = new Nullable<String>()
if (phoneNumber != null)
// == if (phoneNumber.hasValue())
Kääntäjä voisi tarkastaa, ettei mitään nullableksi määriteltyä muuttujaa käsitellä ilman null-tarkistusta ja kaikista muista muuttujista tarkistuksen tarve poistuisi: Näin päästäisiin eroon null pointtereista ja tsiljoonasta turhasta if (x == null)-tarkistuksesta.
2 c. Default-näkyvyys poistettaisiin ja luokkatasoisilta muuttujilta myös protected
Muuttujat ja metodit olisivat defaulttina privaatteja. Avainsanana “private” poistuisi kokonaan.
public class Person
{
private String name
Protected ja default-näkyvyydet muuttujilla lähes poikkeuksetta vain heikentävät koodin luettavuutta ja mielestäni rikkovat omalta osaltaan muuttujien kapseloinnin.
Olen ehkä saanut liikaa vaikutteita lueskeltuani funktionaalisia kieliä, mutta mielestäni nämä säästäisivät paljolta rumalta koodilta. Esimerkiksi metodin sisällä ei tarvitsisi olla tilaansa vaihtavia muuttujia tai null-sijoituksia laisinkaan. Ne ovat yleensä aina merkki siitä, että koodi kaipaisi refaktorointia.
3. Tyypittämättömien kokoelmaluokkien käyttö tulisi estää
Jos välttämättä haluaa tyypittämättömiä kokoelmia, niin se tulisi tehdä tietoisesti:
List<? extends Object>
4. Listojen ja taulujen alustukseen vastaava syntaksi kuin taulukoille.
Taulukko:
String[] greeting = { "terve", "maailma" }
Lista:
List<String> greeting = [ "terve", "maailma" ]
Taulu:
Map<Integer, String> greeting = [ 1:"terve", 2:"maailma" ]
Tähänkin tosin auttavat Java 5:n varargsit.
Vielä kun Sun saisi genericsit optimoitua, niin päästäisiin taulukoistakin eroon.
5. Defaultit metodien parametreille
Säästäisi paljon kirjoittamista:
public greet(String text)
{
greet(text, 5);
}
public greet(String text, int seconds)
{
greet(text, seconds, 1);
}
public greet(String text, int seconds, int repeatCount)
{
// actual greeting code...
}
Näyttäisi seuraavalta:
public greet(String text, int seconds=5, int repeatCount=1)
{
// greeting code
}
Mutta sitä voisi edelleen kutsua ilman vakioarvoiksi määriteltyjä kenttiä:
greet("terve maailma!")
6. Useamman rivin String
String greeting = @"
Terve
maailma!"
7. Vahvasti tyypitetty XPath-haku olioille
Tyyliin:
Bookstore bookstore = new Bookstore();
...
List<Author> authors = bookstore/books[price>35.00 and lang='fi']/author;
Haku myöskin väistelisi kaikki null-viittaukset.
Microsoft on kehitellyt vähän vastaavaa ideaa tulevaan C#-versioonsa. Siinä on vain päädytty SQL-tyyliseen malliin, joka on varsin verboosi, eikä mielestäni yhtä selkeä kuin XPath.
Noin. Nyt kaikki ihmettelevät, että mihin jäivät closuret, continuaatiot, operator overloading, mixins, dynaaminen tyypitys, design by contract, dynaamiset konstruktorit ja tsiljoona muuta hienoa skriptikielten ominaisuutta. No, olen vain jotenkin oppinut elämään niitä ilmankin. Lisäksi esimerkiksi dynaaminen tyypitys ja operator overloading ovat mielestäni jokseenkin vaarallisia ominaisuuksia ja potentiaalisesti aiheuttavat enemmän harmia kuin iloa.
Closuret tosin olisivat kyllä joskus ihan käteviä.
Tulee varmaan 102 uutta ominaisuutta heti mieleen, kun julkaisen tämän, mutta jatketaan vaikka sitten aiheesta toisella kertaa.
Artikkeli on luettu 891 kertaa. Kuuluu luokkiin: Java, Ohjelmointi
6 Kommenttia Lisää kommentti
1. Artti Jaakkola | Heinäkuu 29th, 2006 at 21.16
Joku muukin on näköjään kirjoittanut lähestulkoon identtisen listan toisaalla:
http://bagotricks.com/blog/2006/06/16/a-tale-of-two-javas/
1a -> “Number literals default to being arbitrary precision. Like BigDecimal or maybe a bit more serious.”
2a -> “Everything not-null by default.”
2c -> “Members are private by default.”
4 -> “Maybe JSON-esque object, map, and list literals”
Tässä listassa oli muitakin hyviä ideoita, kuten esimerkiksi “==”-notaation muuttaminen nulleja sietäväksi equalsiksi kuten Groovyssä ja charactereiden muuttaminen int-tasoisiksi (32-bittinen unicode).
2. Artti Jaakkola | Heinäkuu 30th, 2006 at 10.07
Löysin hyvän selostuksen, mitä vaaditaan uuden kielen toteuttamiseksi Eclipseen:
http://wiki.eclipse.org/index.php/The_Official_Eclipse_FAQs#Implementing_Support_for_Your_Own_Language
3. Juha Komulainen | Heinäkuu 30th, 2006 at 10.59
Dang, kirjoittelin jo pitemmänkin kommentin, mutta sitten softa valitti tarkistuskoodin olleen väärin ja menetin sen.
(Siitäs sain kun en noudattanut sääntöä numero 1: kopioi aina kirjoittamasi teksti leikepöydälle ennen kuin klikkaat submittia.) No koitetaan uudestaan, hieman lyhyemmin.
Oletko sattunut tutustumaan Scalaan? Scala on ehkä lupaavin JVM-pohjainen kieli, johon olen törmännyt ja se ratkaiseekin melko monet näistä ongelmista.
Microsoftin LINQ on itse asiassa paljon mukavampi miltä se näyttää: kääntäjä ei itse asiassa ymmärrä lainkaan LINQ-lauseiden semantiikkaa, vaan ainoastaan muodostaa parse-puun, johon pääsee ajonaikana käsiksi ohjelmallisesti. Näin ohjelmoija voi itse määritellä haluamansa semantiikan LINQ-lauseelle. (Esimerkiksi generoida siitä SQL- tai XPath-kyselyn.)
Guy Steelen Fortress menee vielä astetta pitemmälle: Fortressissa voi määritellä ohjelmallisia makroja, jotka jäsentävät ohjelmaa ja generoivat mielivaltaisia rakenteita jo käännösaikana. Ehkä tämä on se Fortressin ominaisuus, jossa Steelen Lisp-tausta näkyy parhaiten.
Olen kyllä menettänyt melkein toivoni Sunin suhteen. Siinä missä Microsoft kehittää omaa alustaansa jatkuvasti (LINQ, F#, …), sanoo Sun mahdollisesti lisäävänsä tuen propertyille, metodiviitteille ja closureille Java 7:aan.
No, ehkä pitää antaa Sunille anteeksi Fortressin takia, vaikka se onkin suunnattu vähän eri kohderyhmälle. (Eikä siis toimi JVM:llä.)
Tulipas tästä nyt linkkipommitus. Anteeksi.
4. Artti Jaakkola | Heinäkuu 30th, 2006 at 19.20
En tiennytkään, että Microsoft on tehnyt oman versionsa OCamlista. Vaikka Haskell taitaakin olla tällä hetkellä kehittyneimpänä pidetty staattinen FL, niin mielestäni tuo OCaml on jotenkin selkeämpi. Johtuu ehkä siitä, että lukemani Haskell-selostus kertoi noin kymmenen erilaista tapaa tehdä yksi ja sama rekursio. Tuli vähän liikaa tavaraa kerralla.
OCamlin tutoriaali taas oli erittäin selkeä:
http://www.ocaml-tutorial.org/
Javan päälle rakennetuista kielistä olen kolunnut vasta Groovyä ja Niceä. Groovyn ongelma alkaa mielestäni olla se, että siinä on turhaan ruvettu sekoittamaan Javan ja Rubyn syntaksia. Hyvänä esimerkkinä viimeaikainen @Property/def-säätö. Nyt eri näkyvyydet menevät seuraavasti:
http://docs.codehaus.org/display/GroovyJSR/Property+proposal
Ihan hyvä, että tiputtivat tuon @Propertyn.
Yksi mikä on kanssa odotellut testaamista on SISC, R5RS-toteutus. Näyttäisi olevan varsin pätevästi toteutettu:
http://sisc.sourceforge.net/r5rs_pitfall.php
Tuo Scala vaikuttaa kyllä varsin tyylikkäältä. Täytyykin ladata se heti. Kiitoksia!
5. Juha Komulainen | Heinäkuu 30th, 2006 at 20.38
OCaml on varmasti helpompi hahmottaa kuin Haskell. OCaml ei ole laiska ja siinä on mahdollisuus sivuvaikutuksiin ilman monadeja. Joku sanoi, että ML on Scheme staattisella tyypityksellä, eikä tuo kaukana olekaan. OCaml lisää tähän pakettiin vielä oliosysteemin.
Voin kyllä silti suositella Haskellia. En usko, että mikään kieli on vaikuttanut ajatteluuni yhtä paljon kuin Haskell. Ensin olin aivan pihalla, mutta nykyään se on ehdottomasti kolmen kärjessä suosikkikielieni listalla. Valitettavasti en osaa sanoa mikä olisi hyvä ensimmäinen Haskell-tutoriaali, mutta hyvä toinen tutoriaali on Jonathan Tangin Write Yourself a Scheme in 48 Hours, jossa toteutetaan Haskellilla Scheme-tulkki.
Groovy on kyllä väärin niin monella tasolla ettei sitä viitsi edes ajatella.
En ole itsekään testannut SISC:iä, mutta Scheme on yksi suosikkikielistäni ja pari vuotta takaperin kirjoittelin itsekin Scheme-toteutuksen JVM:lle. En tiedä tukeeko SISC Java-rajapintojen toteutusta Scheme-koodissa, mutta mikäli tukee, niin se kärsii samasta ongelmasta kuin omakin toteutukseni: tulkilla on monta kontekstia, joidenka välillä continuationit eivät toimi. Käytännössä tämä tarkoittaa sitä, että continuationeita voi käyttää vain triviaaleissa ohjelmissa.
Mutta ongelmia JVM:llä tulee jo ilman continuationeitakin: pelkkä häntäkutsujen optimointi tarkoittaa sitä, että joko koodia ei voi kääntää Java bytekoodiksi tai jokaisessa kutsussa täytyy käyttää trampoliinia. Kumpikaan ei ole oikein kiva ratkaisu.
Ilmeisesti Scheme on Marsista ja JVM Venuksesta. Joka tapauksessa olen luopunut yrityksistä ajaa Schemeä JVM:n päällä.
6. Artti Jaakkola | Elokuu 10th, 2006 at 22.41
C#:n “using” avainsana olisi myös aika mainio. Se takaa, että nimetyn muuttujan Dispose-metodia kutsutaan blokin päätteeksi.
Esim:
Javassa sama homma vaatii enemmän nakuttelua, eikä kieli itsessään mitenkään varmista resurssien vapauttamista:
Jätä kommentti
Sallitut HTML-elementit:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed