Paradigms

Rather than trying to pin down what programming paradigms like ‘object-oriented’ or ‘functional’ are, I will write about what I think is important in programming and then discuss how paradigms help or hinder writing programs properly.

Loosely coupling the environment

To work for us, programs must interact with their environment. The program could be running inside a virtual machine inside a laptop. The laptop may be part of a network that contains other devices and the network may be connected to the internet. All of that is the environment the program can interact with. If it doesn’t, it better not run.

Interactions with the environment make programming more difficult. Code reuse requires a similar environment to work properly. In particular, testing interactive programs requires a realistic test environment. The code of an interactive program doesn’t show what is happening in the environment. Therefore maintainers must use their imagination or rely on documentation to figure out what a program does. Similarly, static analysis tools like type checkers miss a lot of information that might help in finding bugs.

The best way to deal with the environment in a program is, therefore, to split off the parts that interact with the environment into separate modules. The result is a pure core that is loosely coupled to the environment. The smaller the interactive modules are, the larger the part of the program that is easy to work with.

The cost of loose couplings

Loosely coupling the environment has costs that are unavoidable no matter what language or paradigm you use. The most obvious is that the loose couplings must be traversed for the program to have its interactions. A more technical problem is that performing computations requires interactions with the memory and processors of the machine the program is running on, whether those computations are pure or not. Let’s go into more detail here.

Suppose the code says that the program reads a file, processes its data and writes the results to another file. To become a program, the code has to pass a translator, like a compiler that translates it into a machine language, or an interpreter that translates it into direct action. The translator has to generate both the interaction needed for reading and writing files, but also the interactions needed to store the data and the results in memory and to perform calculations in the processors. A smart compiler could rewrite the source code to undo the loose couplings and to add the extra interactions to produce efficient machine code. A smart compiler is a slow compiler, however. A faster but less smart translator will leave more work to be done while the program is running. Either way, we pay for the convenience of loosely coupling the environment.

Comparing paradigms

Programming paradigms are not rigorously defined. Programming languages are almost never simply imperative or declarative but combine these aspects in the paradigms they support. For example, imperative Java code consists of class, interface, and method declarations which are preserved by the Java compiler. Java is generally considered an object-oriented language, so apparently, the object-oriented paradigm admits both imperative and declarative elements. Moreover, no matter what language or paradigm you use, loosely coupling the environment is possible to some extent.

In practice, declarative allows you to go much further. Programs can still be declared to interact with the environment directly, but in most cases, it is just as easy to declare a ‘hole’ where interactions can be injected as needed. The functional programming language Haskell goes a step further by forcing all interactions with the environment to go through the IO-monad, which I imagine encourages writing loosely coupled programs.

Imperative languages, on the other hand, are often ‘close to the metal’. That means ‘tightly coupled to the nearest environment’. They require time and memory management instructions everywhere. In essence, every program is coded as an interaction between the programmer and a virtual machine. This makes the job of a translator much easier–you can have faster compilers that produce faster programs. It also means loose coupling is only possible for the more remote environment. Unfortunately, it requires more code because of all the machine instructions. The result is a slower program unless the translator understands what you intended. This way, imperative languages encourage tightly coupling the remote parts of the environment as well.

Paradigm shift

The imperative paradigms were needed when computers had small memories and slow processors. Both the performance of the translator and the resulting program limited how smart an interpreter could be. These days are past us. Moreover, modern multicore machines require instructions different from those hard-coded in imperative languages that were designed for old-fashioned single-core machines. Those are good reasons to shift away from imperative and towards declarative paradigms.

 

On Monads 2: monads are everywhere

Like most modern scientific theories, the theory of monads is descriptive rather than prescriptive. When applied to programming, the theory is not supposed to tell you how to program nor how to design your libraries or programming languages. Instead the theory helps to describe how a computer composes side effects when it executes a program.

In Java the type signature of a method tells us that it returns a value of a certain type, but that is certainly not all a method can do.

  1. A method can wind up in an endless loop or a deadlock and never return anything.
  2. A method can throw an exception which will abort the computation of the method that is calling it, unless caught. Java also allow throwing errors, which shut down the virtual machine.
  3. If the return type is a class, the method can return null instead of an instance of that class.
  4. A method can change the state of the object it is defined on, the state of objects that are passed to it as arguments, or the state of global variables. This in turn may change the behavior of all methods dependent on those states.
  5. A method can start a new thread and cause the virtual machine to perform computations after the method returns.
  6. A method can communicate with the world outside of the virtual machine by performing IO operations.

For all of these side effects ordinary mathematical functions between sets don’t model Java property. Instead, we need a ‘Java monad’ to capture all outcomes of a method and to explain how side effects are combined when methods are composed into larger methods.

Resisting monads is futile. You don’t avoid the monads but a ubiquitous language for composing side effects. That means that your refusal to conform makes it harder for your customers to find and recognize solutions to their problems in other people’s code. I grant that this is a prescription, but note that it comes from outside the theory of monads itself.

There are other ways monads could help, however. The Java monad is pretty messy and this can make Java programs hard to understand for programmers and also for static analysis tools. For that reason, good programming style often comes down to ‘writing purer functions’.

  1. Use for-loops instead of while-loops, or better yet, iterate through a collection by passing a closure to a library function.
  2. Don’t use exceptions for business logic.
  3. Avoid using null.
  4. Avoid global variables and make objects immutable.
  5. Don’t write asynchronous code if you don’t have to. Use a futures monad when you must.
  6. Decouple parts of your program that have to perform IO.

These precautions would probably not be necessary if Java had been pure by default and only let programmers access side effects through monads, instead or expecting programmers to have the discipline to avoid them.

On Monads

Monads are like lions. It is easy to show what a lion is, but afterwards the students won’t understand lions or be able to work with them.

I will use a Java8-like syntax and terminology for code. A monad consists of a generic type M<T> and two functions–M<T> unit(T t) and M<U> bind(M<T> mt, Function<T,M<U>> f)–that satisfy the monad laws:

  • bind(unit(x),f) == f(x)
  • bind(x,unit) == x
  • bind(bind(x,f),g) == bind(x,y -> bind(f.apply(y),g))

That is all.

To understand monads you need to know why people use them. Programs are usually composed of smaller programs and monads let you redefine how composition works. This way, you can extend all datatypes with new operators, while preserving existing ones.

Take the a typical list monad for example. Instances of the generic type List<T> have methods for working with lists as part of the generic type itself. The function unit sends and object x to a one element list unit(x). That way it makes list operations available on the object. On the other hand, unit(x.anyMethod(y)) turns the result of any method defined on x into a list. Finally the function bind(l,f) applies the list valued function f to every element of the list l and concatenates the results in order into one big list. That way we can apply list valued functions to lists like we can apply ordinary functions to ordinary objects. Hence List<T> has access to both the methods of T and of List and also to every possible combination of them.

You can use the list monad for non deterministic algorithms, where functions can have several different return values. Many other kinds of functions and other methods of composition have their own monads. For example, a futures monad takes care of non blocking asynchronous computations while hiding the callback hell that usually goes with it.

Working with monads is a matter of remembering that you introduce a new form of composition on purpose and that you usually neither have to leave it behind, nor want to do so. Generally you want to keep using the bind and unit functions to keep everything happening “inside the monad” until “the end of the world”, which is the main method of your program. As always, practice makes perfect.

Opgelicht — vervolg

Het is nu een week geleden dat een oplichter mij belde om mijn computer stuk te maken en mij ervoor te laten betalen. Ik wil nu wat meer vertellen over de scam zelf om anderen te helpen er niet in te trappen. Een goed startpunt is de video Scamming the scammers waar ruwweg dezelfde scam in is opgenomen.

De oplichters pakken hun oplichterij zeer professioneel aan. Ze werken met een script. Alles wat ze doen en vertellen, hebben ze al duizenden keren uitgeprobeerd. Bovendien werken ze in teams. Degene die je aan de lijn hebt kan zich dus vooral met jou bezig kan houden, terwijl een “technicus” je computer te grazen neemt. Dankzij hun training zijn ze in staat om veel kritische vragen te beantwoorden. Ook proberen ze je ervan te weerhouden om zelf wat extra controles uit te voeren tijdens de scam.

De oplichter probeert je over te halen om hen toegang te geven tot je computer. Dat doen ze vaak met de logboeken van Windows.

  • Druk tegelijkertijd op de Windowstoets en “R”. Dan opent de “Uitvoeren” dialoog.
  • Type “eventvwr”. Nu openen zich de logboeken van Windows.
  • Open een map aan de linkerkant en selecteer een icoontje, bijvoorbeeld: Windows logboeken/Systeem. De middelste kolom toont nu het systeemlogboek van Windows.
  • Schrik van de grote hoeveelheid foutmeldingen.

In tegenstelling tot wat de oplichter je vertelt is het normaal dat Windows een hoopt fouten aan zijn logboek toevertrouw. Bovendien stuurt Windows die informatie niet op naar Windows support. Voor remote access laten ze je een legitiem programma van het internet downloaden.

Als de oplichters eenmaal binnen zijn is het doel om je ervan te overtuigen dat de problemen nog veel groter zijn en dat je een abonnement moet kopen om je computer te beschermen. Daarnaast zijn ze er wellicht op uit om malware te installeren en informatie te stelen.

Via “msconfig” kun je een lijst met services op de computer zien. Een deel ervan is gestopt. De oplichter vertelt dat virussen dat hebben gedaan. De oplichter opende Prefetch een map met informatie die Windows gebruikt om bepaalde programma’s en services versneld op te starten. Hij wees op bestanden met de naam “rundll32” en opende de browser met een pagina die waarschuwde door een virus dat zich voordoet als “rundll32”. Om te verklaren dat mijn virusscanner dit virus had doorgelaten liet de oplichter zien dat de scanner staat ingesteld op Windows 8, terwijl ik Windows 8.1 heb. Hij loog dat dat een probleem was dat alleen zijn bedrijf kon oplossen. Het volgende verhaal was dat een abonnement op bescherming was verlopen. Om me daarvan te overtuigen opende hij “certmgr.msc”. Dit is een verzameling van certificaten waar ook verlopen certificaten tussen staan, dus hij kon iets laten zien wat verlopen was.

Ondertussen opende de oplichter af en toe “sysreg”. Pas ermee op. Dit programma regelt de encryptie van gebruikersaccounts. Het laat je ook een nieuw wachtwoord instellen. Hiermee kan de oplichter je buiten je eigen computer sluiten. De oplichter maakt daar overigens geen woorden aan vuil–dit hoeft het slachtoffer niet direct te weten. De oplichter heeft een keer teruggebeld om de te vragen hoe het met mijn computer ging, dus wellicht wilde hij me laten betalen voor het wachtwoord.

De scam is begonnen door het Indische bedrijf Comantra. Comantra belt niet alleen rond om slachtoffer te zoeken, maar zet ook advertenties op internet, zodat mensen met computerproblemen hen opbellen. Het is mogelijk dat andere oplichters hun werkwijze intussen volgen en ook dat Comantra nu onder andere namen opereert.

Opgelicht

Ik was bijna het slachtoffer van een Microsoft support call scam. Gisteren belde iemand me die beweerde bij Microsoft te werken. Mijn laptop verstuurde zogenaamd noodsignalen en hij belde op om het probleem op te lossen. Hij haalde me over om toegang te verschaffen tot mijn PC en vervolgens om via Western Union een betaalopdracht op mijn creditcard te plaatsen.

Western Union belde mij gelukkig op om de betaalopdracht te controleren. Ze constateerde dat het een scam was en annuleerde de betaling. Vervolgens heb ik mijn creditcard laten blokkeren. Ik heb geen geld verloren. Bij het opnieuw opstarten bleek dat mijn harde schijf van een wachtwoord was voorzien buiten mijn kennis. Dit probleem heb ik intussen opgelost. Ik heb mijn computer grondig gescand, maar geen virussen of malware aangetroffen. Ook softwarematig lijkt de scam dus te zijn mislukt. Ik heb mijn wachtwoorden aangepast, wat extra beveiligingen geïnstalleerd en de komende maand zal ik op de hoede moeten blijven.

Hoewel het slechts een dag geleden is vind ik het nu al moeilijk te begrijpen dat ik me heb laten overhalen. Het was denk ik een kwestie van kwantiteit en niet van kwaliteit: de oplichter had heel veel trucjes. Als ik er een doorzag dan was hij alweer met de volgende begonnen. Op al mijn kritische vragen had hij een antwoord. Voor alles wat ik moest doen en alles wat hij deed had hij een uitleg. Daarnaast was ik nieuwsgierig en ongeduldig en had ik weinig waardering voor wat ik te verliezen had. Daarom negeerde ik een hoop verdacht gedrag.

Toen ik mezelf het hele verhaal aan Western Union hoorde uitleggen schaamde ik me meteen voor mijn stompzinnigheid. De oplichter had net opgehangen omdat hij het wachten op een betalingsbevestiging van Western Union beu was. Hij hoopte wellicht dat ik mijn laptop onbeheerd zou achterlaten, zodat hij meer rottigheid kon uithalen. Of hij had een ander slachtoffer op het oog. Toen de muiscursor weer in beweging kwam, zette ik mijn laptop in de vliegtuigstand en zette de toegang tot mijn laptop stop. Kort daarna belde hij weer op om te vragen of mijn computer het nog deed, wat ik bevestigde. Hij hing snel daarna op.

In de hoop alle problemen voor te zijn heb ik meteen een systeemherstel gestart, maar die bleef steken tijdens opnieuw opstarten. Er was plotseling een nieuw wachtwoord nodig dat mij onbekend was. Waarschijnlijk was de oplichter van plan mijn laptop te gijzelen voor losgeld. Hij heeft het gedeelte van het register dat de gebruikersaccounts bewaart laten encrypteren en achter een wachtwoord geplaatst. Windows Recovery kan dan niet zoveel meer doen, omdat alle accounts–inclusief die met de nodige administratierechten–verborgen zijn.

De oplossing is in theorie eenvoudig. Windows maakt een back-up van het register als hij opstart. Door het register van buitenaf te benaderen en de back-up terug te zetten, kon ik mijn computer met hetzelfde register laten opstarten als gisterenochtend. In de praktijk moest ik eerst uitzoeken hoe ik mijn laptop vanaf een USB-stick kon laten starten en dat was met mijn laptop niet eenvoudig. Vervolgens moest ik een systeem op mijn USB-stick zetten waarmee ik op de harde schijf kon kijken. Windows was niet goed afgesloten waardoor de harde schijf niet direct toegankelijk was. In Windows Recovery kon ik Windows alsnog netjes afsluiten, zodat ik kon inbreken op mijn eigen computer. Tenslotte had ik weer geluk. sommige oplichters zijn zich bewust van deze oplossing en wissen alle back-ups van het register, maar dat was bij mij was daar geen sprake van.

Om mezelf te beschermen heb ik onder andere remote desktop uitgezet. Instructies staan op http://www.thewindowsclub.com/remote-desktop-connection-windows. De volgende oplichter zal me eerst moeten overtuigen die weer aan te zetten en mij zo herinneren aan mijn vergissing. Ik raad het iedereen aan.

Business Exception

Een programma in de programmeertaal Java bestaat uit methodes die beloven een bepaalde type data terug te sturen. Er zijn echter uitzonderingen (exceptions) waarmee een methode onder die afspraak uit kan komen. Een business exception is een uitzondering die onderdeel is van de business logic van het programma. Dit lijkt me een antipatroon, namelijk een vorm van “exceptions as control flow”.

Afgelopen weken heb ik me bezig gehouden met een bug van de volgende vorm. Een gebruiker vult een formulier in op een website en drukt op verzenden. Hij krijgt dan een foutmelding en een verzoek om de ingevulde informatie te verbeteren. De foutmelding luidt echter “No bean specified”, terwijl het formulier geen vakje heeft waar de gebruiker een boon kan specificeren.

Java dankt haar naam aan Javaanse koffie. Daarom zijn in Java geprogrammeerde systemen nog wel eens opgebouwd uit bonen. Mijn afdeling is bezig met een upgrade van zo’n systeem. Daarbij is er dus een boon zoek geraakt. Terugvinden is geen probleem. Wel een probleem is dat de gebruiker een rare foutmelding te zien krijgt.

Achter de schermen gebeurt het volgende. Tijdens het verwerken van het formulier controleert het systeem of het formulier correct is ingevuld. Het systeem kijkt naar de inhoud van het formulier en naar de rechten van de gebruiker. Als er iets niet klopt maakt het systeem een business exception. De gebruiker krijgt vervolgens bericht dat hij de inhoud van het formulier moet aanpassen.

Soms kan het systeem het formulier niet verwerken omdat de servers of de verbindingen niet deugen. In dat geval maakt het systeem een technical exception. Na een technical exception krijgt de gebruiker bericht dat de service niet beschikbaar is. Het gaat hier immers om een probleem dat de gebruiker niet op kan lossen.

De rare foutmelding ontstaat doordat één onderdeel van het systeem technical exceptions–waaronder verloren bonen–ten onrechte markeert als business exceptions. Dat is slordigheid geweest, want het systeem kan het verschil tussen technical en business best zien. Het was echter beter geweest om de controle van het formulier helemaal niet met uitzonderingen te doen. Alle overgebleven uitzonderingen zijn dan automatisch technische uitzonderingen en verwarring is niet mogelijk.