new functions Diff and Fdiff
diff --git a/Makefile b/Makefile
index 47ac505..630aab2 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@
 
 TARG=github.com/kr/pretty.go
 GOFILES=\
+	diff.go\
 	formatter.go\
 	pretty.go\
 
diff --git a/diff.go b/diff.go
new file mode 100644
index 0000000..71e88de
--- /dev/null
+++ b/diff.go
@@ -0,0 +1,123 @@
+package pretty
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+)
+
+
+type sbuf []string
+
+
+func (s *sbuf) Write(b []byte) (int, os.Error) {
+	*s = append(*s, string(b))
+	return len(b), nil
+}
+
+
+// Diff returns a slice where each element describes
+// a difference between a and b.
+func Diff(a, b interface{}) (desc []string) {
+	Fdiff((*sbuf)(&desc), a, b)
+	return desc
+}
+
+
+// Fdiff writes to w a description of the differences between a and b.
+func Fdiff(w io.Writer, a, b interface{}) {
+	diffWriter{w: w}.diff(reflect.ValueOf(a), reflect.ValueOf(b))
+}
+
+
+type diffWriter struct {
+	w io.Writer
+	l string // label
+}
+
+
+func (w diffWriter) printf(f string, a ...interface{}) {
+	var l string
+	if w.l != "" {
+		l = w.l + ": "
+	}
+	fmt.Fprintf(w.w, l+f, a...)
+}
+
+
+func (w diffWriter) diff(av, bv reflect.Value) {
+	if !av.IsValid() && bv.IsValid() {
+		w.printf("nil != %#v", bv.Interface())
+		return
+	}
+	if av.IsValid() && !bv.IsValid() {
+		w.printf("%#v != nil", av.Interface())
+		return
+	}
+	if !av.IsValid() && !bv.IsValid() {
+		return
+	}
+
+	at := av.Type()
+	bt := bv.Type()
+	if at != bt {
+		w.printf("%v != %v", at, bt)
+		return
+	}
+
+	// numeric types, including bool
+	if at.Kind() < reflect.Array {
+		a, b := av.Interface(), bv.Interface()
+		if a != b {
+			w.printf("%#v != %#v", a, b)
+		}
+		return
+	}
+
+	switch at.Kind() {
+	case reflect.String:
+		a, b := av.Interface(), bv.Interface()
+		if a != b {
+			w.printf("%q != %q", a, b)
+		}
+	case reflect.Ptr:
+		switch {
+		case av.IsNil() && !bv.IsNil():
+			w.printf("nil != %v", bv.Interface())
+		case !av.IsNil() && bv.IsNil():
+			w.printf("%v != nil", av.Interface())
+		case !av.IsNil() && !bv.IsNil():
+			w.diff(av.Elem(), bv.Elem())
+		}
+	case reflect.Struct:
+		for i := 0; i < av.NumField(); i++ {
+			w.relabel(at.Field(i).Name).diff(av.Field(i), bv.Field(i))
+		}
+	case reflect.Interface:
+		w.diff(reflect.ValueOf(av.Interface()), reflect.ValueOf(bv.Interface()))
+	default:
+		if !reflect.DeepEqual(av.Interface(), bv.Interface()) {
+			w.printf("%#v != %#v", av.Interface(), bv.Interface())
+		}
+	}
+}
+
+
+func (d diffWriter) relabel(name string) (d1 diffWriter) {
+	d1 = d
+	if d.l != "" {
+		d1.l = d.l + "." + name
+	} else {
+		d1.l = name
+	}
+	return d1
+}
+
+
+func fieldLabel(label string, f reflect.StructField) string {
+	if label == "" {
+		return f.Name
+	}
+	return label + "." + f.Name
+}
diff --git a/diff_test.go b/diff_test.go
new file mode 100644
index 0000000..c2e5696
--- /dev/null
+++ b/diff_test.go
@@ -0,0 +1,77 @@
+package pretty
+
+import (
+	"testing"
+)
+
+type difftest struct {
+	a   interface{}
+	b   interface{}
+	exp []string
+}
+
+type S struct {
+	A int
+	S *S
+	I interface{}
+	C []int
+}
+
+var diffs = []difftest{
+	{a: nil, b: nil},
+	{a: S{A: 1}, b: S{A: 1}},
+
+	{0, "", []string{`int != string`}},
+	{0, 1, []string{`0 != 1`}},
+	{S{}, new(S), []string{`pretty.S != *pretty.S`}},
+	{"a", "b", []string{`"a" != "b"`}},
+	{S{}, S{A: 1}, []string{`A: 0 != 1`}},
+	{new(S), &S{A: 1}, []string{`A: 0 != 1`}},
+	{S{S: new(S)}, S{S: &S{A: 1}}, []string{`S.A: 0 != 1`}},
+	{S{}, S{I: 0}, []string{`I: nil != 0`}},
+	{S{I: 1}, S{I: "x"}, []string{`I: int != string`}},
+	{S{}, S{C: []int{1}}, []string{`C: []int{} != []int{1}`}},
+	{S{}, S{A: 1, S: new(S)}, []string{`A: 0 != 1`, `S: nil != &{0 <nil> <nil> []}`}},
+}
+
+
+func TestDiff(t *testing.T) {
+	for _, tt := range diffs {
+		got := Diff(tt.a, tt.b)
+		if len(got) != len(tt.exp) {
+			t.Errorf("diffing % #v", tt.a)
+			t.Errorf("with    % #v", tt.b)
+			diffdiff(t, got, tt.exp)
+			continue
+		}
+		for i := range got {
+			if got[i] != tt.exp[i] {
+				t.Errorf("diffing % #v", tt.a)
+				t.Errorf("with    % #v", tt.b)
+				diffdiff(t, got, tt.exp)
+				break
+			}
+		}
+	}
+}
+
+
+func diffdiff(t *testing.T, got, exp []string) {
+	minus(t, "unexpected:", got, exp)
+	minus(t, "missing:", exp, got)
+}
+
+
+func minus(t *testing.T, s string, a, b []string) {
+	var i, j int
+	for i = 0; i < len(a); i++ {
+		for j = 0; j < len(b); j++ {
+			if a[i] == b[j] {
+				break
+			}
+		}
+		if j == len(b) {
+			t.Error(s, a[i])
+		}
+	}
+}