General musings on programming languages, and Java.

Saturday, September 26, 2009

Which language shall we learn?

Hi Jose,

We decided, while drinking an overly-priced red wine the other night, that I'd help you to learn how to program, but without making it sound complicated. So, I thought I'd show you a few different language syntaxes and let you choose.

You can do anything you want with any of these languages, so feel free to pick the prettiest or whichever makes you laugh, or use whatever other criteria you feel like.

I chose the top-10 current mainstream languages, with the exception of PHP, which is too focused on one kind of task, and added Scala, Haskell and Erlang, because I like them.


1.
import java.util.Scanner;

public class Main {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    System.out.println("Please enter your name.");  
    String line = scanner.nextLine();
    System.out.println("Nice to meet you, " + line + ".");
  }
}

2.
#include <stdio.h>

int main(int argc, char **argv) {
  char name[256];

  printf("Please enter your name.\n");
  scanf("%s", name);
  printf("Nice to meet you, %s.\n", name);
  return 0;
}

3.
#include <iostream>
using namespace std;

int main() {
  string name;
  cout << "Please enter your name.\n");
  getline(cin, name);
  cout << "Nice to meet you, " << name << ".\n";
  return 0;
}

4.
Module example
  Public Sub Main()
    Dim line As String
    Console.WriteLine("Please enter your name.")
    line = Console.ReadLine()
    Console.WriteLine("Nice to meet you, " + line + ".")
  End Sub
End Module

5.
#!/usr/bin/perl -w
use strict;
print "Please enter your name.\n";
my $name = <>;
chomp $name;
print "Nice to meet you, $name.\n";

6.
using System;
class Hello {
  static void Main() {
    Console.WriteLine("Please enter your name.\n");
    var name = Console.ReadLine();
    Console.WriteLine("Nice to meet you, " + name + ".\n");
  }
}

7.
name = raw_input("Please enter your name: ")
print "Nice to meet you, ", name, "."

8.
importPackage(java.util);
scanner = new Scanner(System['in']);
System.out.println("Please enter your name.");
name = scanner.nextLine();
System.out.println("Nice to meet you, " + line + ".");

9.
puts "Please enter your name."
name = gets.chomp
puts "Nice to meet you, " + name

10.
main = do
  putStrLn "Please enter your name."
  name <- getLine
  putStrLn ("Nice to meet you, " ++ name ++ ".")

11.
val name = Console.readLine("Please enter your name.")
println("Nice to meet you, " + name + ".")

12.
Name = get_line("Please enter your name.").
put_chars("Nice to meet you, " ++ Name).

Making Methods Testable

I'm going through my old drafts and deleting or publishing them. This one I no longer agree with, I'd rather rewrite the methods to be side-effect free. And the 'magic' I referred to is probably mocks.

"OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things." -- Alan Kay.

Messaging seems to imply behaviour. From wikipedia: "in object-oriented programming languages such as Smalltalk or Java, a message is sent to an object, specifying a request for action."

It seems fairly trivial that, in, say, Java, Math.cos(double) is not object-oriented. It doesn't take any objects, it doesn't return any objects. Even if double was an object type, it wouldn't be object-oriented. One reason is that it isn't late-bound; the other is that it isn't a message that demands action, it's just a function call.

It's certainly possible to imagine an action that has no effects other than returning a value; I don't dispute that; but it does seem overblown to say "sending the message 'cos' to the Math object", instead of saying that Math has a function called cos, and we're calling that. In fact, function is a much better fit for this, because Math.cos is as close as we can get to a mathematical function. Whenever I say function from now on I'll mean a mathematical function.

Java actually has two implementations of all the maths functions, e.g., Math.cos and StrictMath.cos. We could make it so that which one we used was dynamically configurable (again, I'll cover late binding later), but that doesn't make it any more of an action. It's still a function. I assert that most of what we consider an object's behaviour can be validly and usefully thought of as functions over that object's data. In a simple way, you can see this is true; if an object has state X, and you call method x() on it, that changes the object to be in state Y, and you later return the object to state X and call the method again, it will do the same thing. The big difference between methods and functions seems to be that you can't see what side effects a method causes, without knowing the code.

Methods that have real effects on the world, or change objects, are really actions. Testing these is harder than testing functions. They're also harder to reason about and actually unnecessary.

I can think of two ways of testing them, the standard one of which is to simulate the environment and test for evidence of the action. This has the problem that any unwanted action isn't easily detected, except in the most carefully controlled environments. For example, you might set up a chroot environment for automatically testing a program that manipulates files, and not notice that it erases /etc/passwd (because you never use the chrooted filesystem again), or you might test what a method sends to System.out but neglect to also check System.err.

Another way of testing actions would be to first look at the code differently. Code doesn't do anything. It describes things. A program that writes data to a file doesn't really; it requests that data be written to a file. Intercept all interactions with the outside world. If someone wants to write to System.err, they do it through your testing code. The implementation is a little difficult to conceive; perhaps in Java it has to be done through aspects, or DI, if it is to be reasonably brief.

Once you have all the effects that an action produces, you can see if they match your expectations. There is a complication though; you could end up simulating too much of your environment. If a method asks how big a file is, you need to provide an answer. Ideally you would give a different value for each test, e.g., some exception saying the file is inaccessible, lengths 0, 1, and any other lengths that seem relevant to the method.

What we've actually done is to cocoon the method; we've now made it into a function! The perfect test suite for a method makes the method into a function. Well, that's all well and good for I/O, etc., but how about when we're testing a mutable object - it might have changed more than is usual to test for. E.g.:

public void broken()
{
    setX(5);  // we're happy with this line.
    setY(10); // but this one seems wrong.  We want the test to fail while this line is present.
}
.Let's magically jump in the way and grab a list of changes to the object that this method causes. We know x has been changed and y has been changed, so the test fails. In fact, for this case we never need to execute the actions, because we can see that they fail without executing them. For more interesting cases, we can again simulate the effect. Besides my using this technique to demonstrate a point, it could be used to test real running objects without changing them.

So again, the ideal test suite for a method makes it look like a function. If we adapt our code to explicitly state what effects it should cause, instead of going off and causing them, we then actually have functions in our code. Here's the above broken(), but fixed..

public List<Action> fixed()
{
    return asList(new AssignAction("x",5),new AssignAction("y",10));
}
The code is significantly uglier and doesn't use static typing, but it's now actually a function. Because I've adapted it to return stuff instead of do stuff, it is now far easier to test. Or perhaps all I've done is to make message passing even more explicit. Well, message passing involves actions, and to some extent we can invoke the above actions and observe their results without changing anything (recurse through the list constructing a chain of values, rather like SICP's discussion of chained environments).

It's now clearly far easier to reason about the method, because you can trivially observe all of its side-effects. You could even decide which ones to allow, externally to the code. In the usual case that you want to execute the effects immediately, you can still do that.

Saturday, September 19, 2009

Is it actually type inference we need, or just a better syntax for type declarations?

I've looked a little at Typed Scheme recently, because the main problem I have with Lisp is that it's untyped, but I lack the imagination and skill to make a typed version.

Typed Scheme has local type inference, like C#'s var, but other than that, if you want it typed, you write it typed. The types go before the declarations, like in Haskell, which provoked a surprising thought, namely the title of this post.

In Scala, Java and C#, type inference always seems desirable. Let's look at how you'd declare a method that takes a list, an int and returns an element of that list in each language:

Scala: def method[A](list: List[A], num: Int): A // the : Float would normally be inferred if left out.

Java: A <A> method(List<A> list, int num)

C#: A method<A>(List<A> list, int num)

Those aren't amazingly bad, but compare them to Python:

def method(list, num)

or Scheme:

(define (method the-list num))

The untyped versions are undoubtedly prettier, though far less informative. And compare now to Haskell:

method list num = ...

Haskell's version uses type inference; the types of list and num will be inferred by their usage in the function. But lots of Haskell programmers will write the type explicitly for all functions. I think they do this because in Haskell the syntax for explicit types is better than in Scala, C# and Java:

method :: [a] -> Int -> a
method list num = ...

You can read that first line as "method takes a list of some type we'll call a, an Int and returns an a."

The specific way in which the Haskell (and Typed Scheme) mechanisms are better is that the type information isn't mixed up in the syntax for naming parameters (and after Scala and C# add optional parameters, that too). It'll never happen for any, but I'd like to see the following syntaxes become possible:

Scala:
method[A] : (List[A], Int) => A
def method(list, num) ..

Java/C#:
A method<A> (List<A>, int)
method(list, num) { .. }

The programming language I'll one day write currently looks a bit like Typed Scheme. Like Typed Scheme, it has local variable type inference, but not function definition type inference. I realise that supporting one but not the other is a bit inconsistent, so I know I have more thinking to do, but Typed Scheme (and Haskell) made me realise not to imitate Scala, C# or Java's type declaration syntax.

Blog Archive

About Me

A salsa dancing, DJing programmer from Manchester, England.