When I find something interesting and new, I post it here - that's mostly programming, of course, not everything.

Friday, October 24, 2008

Java's Strong Typing

In a typical case of user application dealing with some kind of "apache javabean properties access", we still want to have generics help us with strong typing, to keep code safe. For this, we introduce a parameterized class Property<T>. And write something like this:

public class ClientCode {
Object model;
public ClientCode(Object model) {
this.model = model;
}

public void businessLogic() {
Property<String> name = new Property<String>("userName", model);
Property<Date> dob = new Property<Date>("dateOfBirth", model);
Property<Boolean> isGood = new Property<Boolean>("isGood", model);

if (isGood.value() && dob.value().before(new Date(1990, 10, 24))) {
System.out.println("One good adult user: " + name.value());
}
}

public static void main(String[] args) {
new ClientCode("ignore me").businessLogic();
}
}


Naive but natural. And now the implementation of Property<T>:

package common;

public class Property<T> {
String name;
T value;

public Property(String name, Object container) {
this.name = name;
this.value = (T) "hello world";
//        org.apache.common.weird.helper.util.BeanAccessUtil.
//            getProperty(container, name);
}

public T value() { return value; }
}


Looks pretty convincing, right? But surprise! Do you think this code will throw a ClassCastException from within Property<T>? Seems like it should; we even have a variable that, in case of it being type Date, cannot accept a string in assignment. Right?

Wrong...

As you know, generic type information is lost in compilation. As a result, all the compiled class knows about value type is that it is something. It is actually an Object, and can accept whatever is assigned. More, the method value() also returns an Object. But the client is sure, due to generic parameterization, that it returns the expected type. But it does not. And the exception is thrown right in the client code.

Monday, October 20, 2008

Experimenting with Covariance and Contravariance in Java Generics

Here I'll just publish the code that demonstrates what is accepted and what is not by the compiler when you use generics. Not many comments, since most of the code is extremely trivial.

Oh, and definitions. An expression is contravariant in variable x of class X if x can be substituted with an instance y of class Y which is a subclass of X. An expression is covariant in variable x of class X if x can be substituted with an instance y of class Y which is a superclass of class X.

E.g. (a = f(b)) is contravariant in b and covariant in a.

Let's have the following class inheritance diagram ('=>' means iheritance):

E => C, D => C, C => B, B => A

In details,

class A {
String id;

A(String id) { this.id = id; }
}

class B extends A {
B(String id) { super(id); }
}

class C extends B {
C(String id) { super(id); }
}

class D extends C {
D(String id) { super(id); }
}

class E extends C {
E(String id) { super(id); }
}


And let's have an interface and a class, something similar to Map and HashMap:

interface M<X, Y> {
void put(X x, Y y);
Y get(X x);
}

class M1<X, Y> implements M<X, Y> {
M1() {};
public void put(X x, Y y){};
public Y get(X x){ return null; }
}


The following code demonstrates a ubiquitous and pretty legal usage of covariance and contravariance.

First, the example where we vary the second parameter ("the value"), and provide an exact knowledge of its type on declaration (and instantiation):

void testParameter2() {
M<String, B> mSB = new M1<String, B>();
mSB.put("", new B(""));
mSB.put("", new C(""));
B aa = mSB.get("");
M<String, C> mSC = new M1<String, C>();
mSC.put("", new C(""));
B b = mSC.get("");
C c = mSC.get("");
}


Now let's try a contravariant version of wildcard in the second parameter of M. Turns out that using wildcard duly blocks some of the "expected" functionality.

void testParameter2WithExtendsWildcard() {
M<String, ? extends C> mSCx = new M1<String, D>();
mSCx = new M1<String, E>();
// Actually, only E should be accepted, but this information is lost on assignment.
// So the next line won't compile:
mSCx.put("", new C(""));
// put(java.lang.String,capture#660 of ? extends common.VarianceTest.C) in
// common.VarianceTest.M<java.lang.String,capture#660 of ? extends common.VarianceTest.C>
// cannot be applied to (java.lang.String,common.VarianceTest.C)

//
// And we don't know if it is D (it is not).
// So the next line won't compile:
mSCx.put("", new D(""));
// put(java.lang.String,capture#511 of ? extends common.VarianceTest.C)
// in common.VarianceTest.M
// cannot be applied to (java.lang.String,common.VarianceTest.D)

B b = mSCx.get("");
C c = mSCx.get("");
}


Trying the same, but 'super' instead of 'extends':


void testParameter2WithSuperWildcard() {
M<String, ? super C> mSCs = new M1<String, C>();
mSCs = new M1<String, B>();
// We don't know what's hiding behind 'super', maybe it's Object
// So the next line won't compile:
mSCs.put("", new B(""));
// put(capture#701 of ? extends common.VarianceTest.C,java.lang.String)
// in common.VarianceTest.M
// cannot be applied to (common.VarianceTest.C,java.lang.String)

mSCs.put("", new C(""));
mSCs.put("", new D(""));
// We don't know what's hiding behind 'super', maybe it's Object
// So the next line won't compile:
B b = mSCs.get("");
// incompatible types found
// : capture#222 of ? super common.VarianceTest.C required: common.VarianceTest.B

Object any = mSCs.get("");
}


The same with parameter 1. No surprises here:

void testParameter1() {
M<B, String> mBS = new M1<B, String>();
mBS.put(new B(""), "");
mBS.put(new C(""), "");
String s = mBS.get(new B(""));
s = mBS.get(new B(""));
M<C, String> mCS = new M1<C, String>();
mCS.put(new C(""), "");
mCS.put(new D(""), "");
s = mCS.get(new C(""));
s = mCS.get(new D(""));
}


The following example shows that it is totally useless to specify 'extends' wildcard in contravariant position:

void testParameter1WithExtendsWildcard() {
String s;
M<? extends C, String> mCxS = new M1<C, String>();
mCxS = new M1<D, String>();
// Who knows what is the actual type of param1...
// The following two lines do not compile:
mCxS.put(new C("");
// put(capture#701 of ? extends common.VarianceTest.C,java.lang.String)
// in common.VarianceTest.M
// cannot be applied to (common.VarianceTest.C,java.lang.String)

mCxS.put(new D(""), "");
// put(capture#701 of ? extends common.VarianceTest.C,java.lang.String)
// in common.VarianceTest.M
// cannot be applied to (common.VarianceTest.D,java.lang.String)


// Same here, parameter type unknown
// The following two lines do not compile either:
s = mCxS.get(new C(""));
// get(capture#897 of ? extends common.VarianceTest.C) in common.VarianceTest.M // of ? extends common.VarianceTest.C,java.lang.String>
// cannot be applied to (common.VarianceTest.C)

s = mCxS.get(new D(""));
// get(capture#897 of ? extends common.VarianceTest.C) in common.VarianceTest.M // of ? extends common.VarianceTest.C,java.lang.String>
// cannot be applied to (common.VarianceTest.D)


}


Now let's try the same with 'super' instead of 'extends'. This means that an instance of any superclass instance can show up (down to Object), we just have no way to know which one it is:


void testParameter1WithSuperWildcard() {
M<? super C, String> mCsS = new M1<C, String>();
mCsS = new M1<B, String>();
// Who knows what should be the actual type of param1...
// The following line does not compile:
mCsS.put(new B(""), "");
// put(capture#248 of ? super common.VarianceTest.C,java.lang.String) in
// common.VarianceTest.M
// cannot be applied to (common.VarianceTest.B,java.lang.String)

// C can be cast to any superclass of C
mCsS.put(new C(""), "");
mCsS.put(new D(""), "");
String s = mCsS.get(new C(""));
s = mCsS.get(new D(""));
s = mCsS.get(new C(""));
// Who knows what should be the actual type of param1...
// The following line does not compile:
s = mCsS.get(new B(""));
// get(capture#330 of ? super common.VarianceTest.C) in common.VarianceTest.M // of ? super common.VarianceTest.C,java.lang.String>
// cannot be applied to (common.VarianceTest.B)

}


Generics with wildcards are not broken. The lack of understanding on how they work stems from the lack of understanding of covariant and contravariant substitution. Once you grasp it, the rest is easy.

And there's something specific about wildcards. If it is ? extends A, it does not mean that anything extending A can substitute. It means that there is something that extends A, and there' no way to figure out what.

Monday, October 06, 2008

Unlimited Cartesian Product in Java - part 2

(This is the second part, see part 1).

Here is the code that implements Cartesian product in Java. The implementation amounts to providing the following methods of class Cartesian:

 
public static <X, XS extends Iterable<X>>
Iterable<Iterable<X>> product(final XS... xss) {
return product(Arrays.asList(xss));
}

public static <X, XS extends Iterable<X>>
Iterable<Iterable<X>> product(final Iterable<XS> xss) {
return new Cartesian<X, XS>().calculateProduct(xss);
}


So we could write code like this:


Iterable<Iterable<Integer>> theProduct =
Cartesian.product(Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6)));
assertEquals(9, Set(actual).size());


and like this:


Iterable<Iterable<Integer>> theProduct =
Cartesian.product(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6));


Note that we could not use Iterable<Iterable<X>>, since, imagine, in this case we could not even build a product of Set<Set<X>>.

How do we calculateProduct? The idea is that we define it by recursion. If we have X0, X1,..., Xn, we define product(X0, X1,..., Xn) as X0 x product(X1, X1,..., Xn); and if the sequence is actually empty, we return a singleton,
new ArrayList<Iterable>() {{
add(Collections.EMPTY_LIST);
}};
(I use crazybob's contraption to build the singleton).


private Iterable<Iterable<X>>
calculateProduct(final Iterable<XS> xss) {
if (xss.iterator().hasNext()) {
Pair<XS, Iterable<XS>> pair = split(xss);
XS head = pair.x();
Iterable<XS> tail = pair.y();
return appendComponentToProduct(head, calculateProduct(tail));
} else {
return new ArrayList<Iterable<X>>() {{
add(Collections.EMPTY_LIST);
}};
}
}


Here I use two methods, split and appendComponentToProduct. The first one is obvious (okay, it's an exercise if you are a beginner); the second one is here. See what it does: we have a component, X0, say,
[1, 2, 3]
, and a product of previous components X1 x ... x Xn, which is an iterable of iterables, e.g. [[4, 5], [5, 6]]; we need to append elements of X0 to all elements of the partial product. That appendage function is called consWith: it takes an x and appends it to all partial product elements. We apply this function to the whole X0; what we receive has to be flattened to produce a list of tuples [x0, x1, ..., xn]


private Iterable<Iterable<X>>
appendComponentToProduct(
XS component,
Iterable<Iterable<X>> partialProduct) {
// E.g. [1, 2, 3], [[4, 6], [5, 6]] ->
// [[[1, 4, 6], [1, 5, 6]], [[2, 4, 6], [2, 5, 6]], [[3, 4, 6], [3, 5, 6]]] ->
// [[1, 4, 6], [1, 5, 6], [2, 4, 6], [2, 5, 6], [3, 4, 6], [3, 5, 6]]
return flatten(consWith(partialProduct).map(component));


Flattening is, again, a trivial exercise; let's take a look at our consWith. It is a method that returns a function (which we apply to the component X0:


public <ElementOfProduct extends Iterable<X>>
Function<X, Iterable<Iterable<X>>>
consWith(final Iterable<ElementOfProduct> pxs) {
return new Function<X, Iterable<Iterable<X>>>() {
// takes an x, returns a function appending x to sequences
public Iterable<Iterable<X>> apply(final X x) {
// Takes a sequence [x1, x2, ...], returns [x, x1, x2, ...]
return new Function<ElementOfProduct, Iterable<X>>() {
public Iterable<X> apply(ElementOfProduct y) {
return concat(Arrays.asList(x), y);
}
}.map(pxs);
}
};
}


A little bit dizzy? It's okay. We have arrived to the summit.

This is Higher Order Java!

Wednesday, October 01, 2008

Unlimited Cartesian Product in Java - part 1

You probably know what Cartesian product is. Take two sets, A and B, their product, A x B, is a set of all pairs (a, b), where a belongs to A and b belongs to B. Something like that.

Here I am going to show how to build a Cartesian product for an unlimited collection of sets, or rather for an Iterable of Iterables. Unlimited means any number starting from 0. Due to limitations of Java, all these component sets (or rather component Iterables, will have the same element type.

More formally, given a sequence of sets (or of sequences), A0, A1, ... An, we will produce A = A0 x A1 x ... x An, whose elements are sequences (a0, a1, ..., an), where each ai belongs to Ai.

So, for instance if we have just A0 and A1, their product will be the following: {(a0, a1}.

With this definition, we, with some confusion, have to admit that the Cartesian product of just one A0 is a set of singletons, {(a)} for all a in A. Of course there is a canonical one-to-one correspondence between A and its "product".

More curious is the question what exactly is the product of 0 of components. In a category with limits it is a terminal object, 1. But how about JVM? There's no such thing as a terminal object.

No problem. We can use our definition, and take the set of empty sequences. There's just one empty sequence, so our canonical singleton has the following form:
{{}}. Did you notice the underlying type is not even mentioned. We have an Iterable of one single empty iterable of what type? Actually, Java deduces it from the type of the variable the product is assigned to. That's the safest way to pass the type knowledge into the executing code.

So, the solution I propose is to have something like this:

Iterable<Iterable<Integer>> actual = Cartesian.product(Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)));
assertEquals(9, Set(actual).size());


Note the funny function name: Cartesian.product. Why do we need the special class named Cartesian? For two reasons. One, it is a neat way to provide type parameterization; second, calculating the product is not an elementary operation, so we need a bunch of private methods to help us. In JavaScript this can be accomplished by defining functions within our function; in Haskell we could comfortably add to our main definition the auxiliary "where" definitions. In Java, the "where" become class or instance methods.

So, we are introducing a special class named Cartesian, with just one exposed method, product:


public static class Cartesian<X, XS extends Iterable<X>> {
...
public static <X, XS extends Iterable<X>> Iterable<Iterable<X>> product(final Iterable<XS> xss) {
return new Cartesian<X, XS>().calculateProduct(xss);
}
...


You've noticed the IT parameter type. The reason for having this parameter is that we can pass for product calculations all kinds of Iterables of Iterables: Sets of Lists, Lists of Sets... any combination would work.

First, the algorithm. Here's how lj user=_navi_ defines it:

product :: [[a]] -> [[a]]
product [] = [[]]
product (xs:xss) = product' xs (product xss)
where
product' xs pxs = concat $ map (\x -> map (x:) pxs) xs


In plain words, for us, Java pdestrians, it means this:

Say, we have an Iterable consisting of the first component, and the tail. And suppose we already have a product (call it pxs) built for the tail.
Then we do the following: scan the first component, A0 attaching its elements ai to the head of each element of the already built product.

We do this by having a function (let's call it f(a0, as) that concatenates an element a0 with the tail [a1, ..., an], producing a new sequence [a0, ..., an].
Having function f, we build another function, g(a) that builds a list of all [a0, ..., an] - by mapping the product of the tail, pxs using the function f: g returns, for each a0, a list of concatenations built by f.

So, for each element a the function g produces a list. Now let's apply this to all the elements of the first component, A0: that is, transform the component... to what? To a sequence of sequences of sequences.

We do not need such a deep structure. We just need the sequences of the lower level, listed in some order or in no order. So we do what is known as flattening the list.

The important thing is that the product we build should be lazy. As an example, suppose we build all permutations of N elements. This is equivalent to building a cartesian product of [0..N-1] x [0..N-2] x ... x [0..1] x [0]; there's N! elements in the product. We do not want to fill our whole memory with these elements before we start processing them.


(the code is published and commented in the second part)

Monday, September 29, 2008

class Function<X, Y>

As we all know, functions are not a part of Java language; and closures are banished to the North, to Microsoft. What do we, humble Java programmers, do? We invent a Function class. Not that it suddenly makes Java a functional programming language, but it gives us the opportunity to express things in a more concise way, using higher level terminology.

In this article I use Pair class, see details here.


public abstract class Function<X, Y> {
public abstract Y apply(X x);
}


If that were all that we wanted, we could as well make it an interface. There is a certain trend to first introduce an interface, and then an abstract base class implementing this interface and providing the base functionality. But... how many different ways of defining a composition of two functions do we know? Exactly one, right? See, there are certain rules and properties, without which a function would not be a function. Let's make them a part of the abstract class, since there's no choice anyway.


public static <X, Y, Z> Function<X, Z>
compose(final Function< f, final Function<Y, Z> g) {
return new Function<X, Z>() {
public Z apply(X x) {
return g.apply(f.apply(x));
}
};
}

public static <T, T1 extends T> Function<T1, T> id() {
return new Function<T1, T>() {
public T apply(T1 t) { return t; }
};
}


We will be tempted to apply a Function to an Iterable, a Collection, a Set, a List.

The naive way would be to write something like this:


Function<X, Y> f = ....
....
List<X> xs = ...
...
List<Y> ys = new ArrayList<Y>();
for (X x : xs) {
ys.add(x);
}
// good job!


I have three problems with this kind of code.

Problem 1. It runs an explicit loop. Did you ever ask yourself what is the reason programmers have been using loops in their code? Are not all the use cases already known, so that we could just mention the case, and the loop is magically applied? Want an example? String concatenation. Nobody writes a loop to concatenate two strings.

Problem 2. We may not even need all the results in the new array, ys - why bother calculating the values? Would be cool to calculate them when needed.

Problem 3. Although an average computer these days has more memory than an average programmer can fill, it can happen that the source array is exceptionally long; and if we fill each temporary array with data, we may be soon out of memory. And for what reason? For no reason, just because we don't know better.

But we do.

The transformed set can be virtual. Built on demand. Let's start with applying a Function to an Iterator. What are we going to obtain? Another Iterator, with exactly the same number of values, but the values are different.


public Iterator<Y> map(final Iterator<X> source) {
return new Iterator<Y>() {

public boolean hasNext() {
return source.hasNext();
}

public Y next() {
return apply(source.next());
}

public void remove() {
source.remove();
}
};
}


Iterators rarely materialize themselves in the code; they use to be returned by Iterables; so let's apply the same trick to Iterables.


public Iterable<Y> map(final Iterable<X> source) {
return new Iterable<Y>() {
public Iterator<Y> iterator() {
return map(source.iterator());
}
};
}


We may not feel comfortable if the resulting Iterable's iterator() is called multiple times; but in this case we can eventually decide to cache the values. Or maybe not, if there's a billion of them, and the function is inexpensive to calculate.

The only difference between a Collection and an Iterable is that in the case of Collection we know the size. So there.


public Collection<Y> map(final Collection<X> source) {
return new AbstractCollection<Y>() {
public Iterator<Y> iterator() {
return map(source.iterator());
}

public int size() {
return source.size();
}
};
}


For List, we throw in one more method, get(int):


public List<Y> map(final List<X> source) {
return new AbstractList<Y>() {
public Y get(int index) {
return apply(source.get(index));
}

public Iterator<Y> iterator() {
return map(source.iterator());
}

public int size() {
return source.size();
}
};
}


I wonder if anybody can come up with a solution that does not involve copy-pasting iterator() implementation.

Now, you have probably noticed that all these transformation methods are not static "util", but are a part of Function class definition. As a result, if you have a Function f, you can write in your code List<Y> results = f.map(souceList);

Applying the same trick to a Set is not exactly fair. Set "contract" say that we should not have repeating values; and in our case there is no guarantee that we would not:


public Set<Y> map(final Set<X> source) {
return new AbstractSet<Y>() {
public Iterator<Y> iterator() {
return map(source.iterator());
}

public int size() {
return source.size();
}
};
}


On certain occasions, though, the uniqueness of the function values is guaranteed, like in the case of Schwartzian Transform. Schwartzian transform consists of producing pairs (x, f(x)) for a given collection of x's. This operation is known to be useful in the times of Perl language; I found it useful in Java too.


private static <X, Y> Function<X, Pair<X, Y>> schwartzianTransform(final Function<X, Y> f) {
return new Function>() {
public Pair<X, Y> apply(final X x) {
return LazyPair(x, f);
}
};
}


It is kind of funny that I have to define this method as static; one of us (me or java) is not smart enough to build new functions inside instance methods. Feel free to go ahead and try.

All of this allows us, given a Function and a Set, build a Map:


public Map<X, Y> toMap(final Set<X> keys) {
// this innocent function makes a Pair look like a Map.Entry
Function<Pair<X, Y>, Map.Entry<X, Y>> fromPairToMapEntry = id();
final Function<X, Map.Entry<X, Y>> pairBuilder =
compose(schwartzianTransform(this), fromPairToMapEntry);
return new AbstractMap<X, Y>() {
public Set<Entry<X, Y>> entrySet() {
return pairBuilder.map(keys);
}
};
}

Monday, September 22, 2008

Java Pair: a closer look

Who did not implement a Pair class, he did not write code yet. Or she.

People tired of implementing it, beg Sun to add Pair to the library (bugs 6229146, 4947273, 4983155, 4984991). Sun decided not to fix it. So the whole world under the Sun does.

A cheap solution would be to have a record with two fields, first and second, and implement equals() to compare both fields, and hashCode() as something like (first * 31) ^ second (computer people believe in magic "randomizing" properties of xor and number 31) so that one would be able to freely change the values.

Convenient, but wrong. What if you want to use pairs as keys in a map? You put an entry in the map, then change the key content, and kaboom! entry not found. A funny but working solution could be to freeze the hashCode on creation, getting them from another source of randomness in Java world, System.currentTimeMillis(): one can easily build half a million records within one milliseconds these days.

A better solution would be to have your pair immutable: take constructor parameters, store them, and only return them, but never change them. This, with a decent implementation of hashCode() and toString() is a good candidate to enter the imaginary World of Ideal Code.

But there's a nuisance. It would be great if it implemented Map.Entry< - that is, have aliases for getFirst() and getSecond(); first and second are now also known as key and value. Makes life easier if you already have a set of pairs and want to expose it as a map... yes, uniqueness of keys is required.

As an example, imagine that we have a Function<X, Y>, and a set of arguments; to expose this as a map, we have choices: either to instantiate a regular HashMap and fill it with the data; instantiate a Map as an AbstractMap where entrySet() is a prebuilt Set<Pair<X, Y>>, or do something smarter. See, if the key set is really huge, and/or evaluating the function takes some time/resources, we probably do not want to duplicate the keys and run the function over all of them. Who knows how many entries in this map will be needed by the client code.

Things can be even worse: the key set maybe large and/or virtual, so that building a set of pairs is an impossible task. What if it is a set of Natural Number, like this:

public class N extends AbstractSet<Integer> {
public Iterator iterator() {
return new Iterator() {
int n = 0;
public boolean hasNext() {
return true;
}

public Integer next() {
return n++;
}

public void remove() {
throw new UnsupportedOperationException();
}
};
}

public int size() {
return Integer.MAX_VALUE;
}
}


I am sure you do not want to build a set of pairs on such a set.

So let us not assume anything. One pair may be lazy, the other one just instantiate from two given values. We need an abstract class to generalize this.


/**
* Abstract pair class.
* Implementations should implement two methods,
* and may be lazy or regular.
* But in any case this is an unmodifiable pair.
*/
public abstract class Pair<X, Y>
implements Map.Entry<X, Y> {
public abstract X x();
public abstract Y y();

public X getKey() {
return x();
}

public Y getValue() {
return y();
}

public Y setValue(Y value) {
throw new UnsupportedOperationException();
}

private boolean equal(Object a, Object b) {
return a == b || a != null && a.equals(b);
}

public boolean equals(Object o) {
return o instanceof Pair &&
equal(((Pair) o).x(), x()) &&
equal(((Pair) o).y(), y());
}

private static int hashCode(Object a) {
return a == null ? 42 : a.hashCode();
}

public int hashCode() {
return hashCode(x()) * 3 + hashCode(y());
}

public String toString() {
return "(" + x() + "," + y() + ")";
}
}


From this class we can build a couple of concrete classes:


public class BasePair<X, Y> extends Pair<X, Y> {
private final X x;
private final Y y;

public X x() { return x; }
public Y y() { return y; }

private BasePair(X x, Y y) {
this.x = x;
this.y = y;
}

public static <X> BasePair<X, X> Pair(X[] source) {
assert source.length == 2 :
"BasePair should be built on a two-element array; got " +
source.length;
return new BasePair(source[0], source[1]);
}
}


BasePair is built from either two values or a two-element array; do we need a list-based constructor? It's possible.


public class LazyPair<X, Y> extends Pair<X, Y> {
private final X x;
private final Function<X, Y> f;
private Y y;
boolean haveY = false;

private LazyPair(X x, Function<X, Y> f) {
this.x = x;
this.f = f;
}

public X x() {
return x;
}

public Y y() {
if (!haveY) {
y = f.apply(x);
haveY = true;
}
return y;
}

@Override
public boolean equals(Object o) {
return o instanceof LazyPair ?
equal(x(), ((LazyPair)o).x()) : o.equals(this);
}

@Override
public int hashCode() {
return hashCode(x);
}
}


LazyPair calculates the function value only once, and caches it.

This seems to be all on this trivial issues. Thanks for reading!

Wednesday, September 17, 2008

Java: have a set of pairs, need a map

In Java, as in may other discourses, maps and sets of pairs with unique keys are more or less the same thing. The problem is that Map in Java is represented as a set of Map.Entry<X, Y>; and Map.Entry is not the best way to represent pairs in your code.

Personally I, probably like the best half of Java programmers, have my own class named Pair<X, Y>; the components are called, yes, x and y. In Map.Entry they are called key and value.

So I had to make Pair<X, Y> implement Map.Entry<X, Y>, so that I would not need to recreate a Map.Entry<X, Y> every time I have a Pair<X, Y>.

Now, if I have a Set<Pair<X, Y>>, what should I do to produce a Map<X, Y>, without replicating all my data? I will need an AbstractMap<X, Y>, and provide a method, entrySet(). I already have a set; the trouble is, this set is a set of pairs, Set<Pair<X, Y>>. Should I replicate it? No way; what if there's 50 million records? Can I cast it? No... it is just impossible. should I wrap it somehow? Yes, kind of.

Luckily, I have some cool classes and methods already that would help me to do the job.

The cool class is called Function<X, Y>. The traditional approach is to declare it as an interface with one method, Y apply(X x). I thought it would be cool to implement additional methods. First, having an Iterator<X>, one would like to map this iterator using a function, right? That's what the method in Function<X, Y>, Iterator<Y> map(Iterator<X> iteratorX is for:

public Iterator<Y> map(final Iterator<X> iteratorX) {
return new Iterator<Y>() {
public boolean hasNext() {
return iteratorX.hasNext();
}

public Y next() {
return apply(iteratorX.next());
}

public void remove() {
iteratorX.remove();
}
};
}


This was the biggest chunk of code.

Now that we can map an iterator, we can as well map an iterable, a collection, a list... but we need a set. Here we go:

public Set<Y> map(final Set<X> setX) {
return new AbstractSet<Y>() {
public Iterator<Y> iterator() {
return map(setX.iterator());
}

public int size() {
return setX.size();
}
};
}


How does it all help me? I just want to map my Set<Pair<X, Y>> to a Set<Map.Entry<X, Y>> without multiplying entities. So we need a function that returns the same pair, but shows it as a map entry. This is an identity function. Let's define it in Function class:

public static <T, T0 super T> Function<T, T0> id() {
return new Function<T, T0>() {
public T0 apply(T t) { return t; }
};
}


See, while it returns the same instance, but, in the eyes of compiler, casts it to some superclass. Now that we have it, let's apply it to solve our problem:


public static <X, Y> Map<X, Y> Map(final Set<Pair<X, Y>> pairs) {
final Function<Pair<X, Y>, Map.Entry<X, Y>> idmap = Function.id();

return new AbstractMap<X, Y>() {
public Set<Entry<X, Y>> entrySet() {
return idmap.map(pairs);
}
};
}


One little note. We had to introduce the variable, idmap - thus mentally "reifying" our identity function; it would not work if we just called Function.id() on spot - Java compiler would be too confused.

Monday, August 25, 2008

How Do You Test JavaScript Loading?

I had a problem: in my JavaScript unittests I wanted to check the behavior of my scripts when they are reloaded multiple times. That's kind of challenging: scripts are loaded in a background thread, and unittests run in the foreground test.

There seems to be no way to synchronize these two processes, to have either an event listener or a wait function that would make sure assertions in the test cases are called after a certain script is reloaded.

Here is what I was looking for:


function loadScript(path) {
var script = document.createElement("script");
script.src = path;
document.body.appendChild(script);
JS_TOOLS.sleep(500);
}

function loadScripts(var_args) {
for (var i = 0; i < arguments.length; i++) {
loadScript(arguments[i]);
}
}

function testReloading_justOne() {
loadScript('scriptToLoad.js');
assertFalse(A_CERTAIN_VARIABLE_FROM_THE_SCRIPT === undefined);
}


The trick is to put the thread to sleep... for a little while.

I looked around on the internet, and found some cheap and some dear advices. In two words: Use Applet.

So I did. I wrote an applet, looking like this:


public class JsTools extends Applet {

/**
* Sleeps given number of milliseconds.
*
* @param timeToSleep time to sleep (ms)
*/
public void sleep(long timeToSleep) {
for (long wakeupTime = System.currentTimeMillis() + timeToSleep;
timeToSleep > 0;
timeToSleep = wakeupTime - System.currentTimeMillis()) {
try {
Thread.sleep(timeToSleep);
} catch (InterruptedException ie) {
// ignore it, just repeat until done
}
}
}
}



The idea is that I may want to add more functionality to this applet some time later.

Now, I compile this applet, and store it in jsapplet.jar (with the right directory structure, so that it could be found by the browser's jvm. Then I wrote some JavaScript code that loads the applet and exports a function, sleep():


function jstools(location) {
var JSTOOLS_ID = "JSTOOLS_ID";
var userAgent = navigator.userAgent.toLowerCase();
var isIE = userAgent.indexOf('msie') != -1 &&
userAgent.indexOf('opera') < 0;

function _(var_args) {
for (var i = 0; i < arguments.length; i++) {
document.write(arguments[i]);
}
};

function openElement(name, attributes) {
_('<', name);
for (var id in attributes) {
_(' ', id, '="', attributes[id], '"');
}
_('>');
}

function closeElement(name) {
_('');
}

function addParameters(parameters) {
for (var name in parameters) {
openElement('param', {name: name, value: parameters[name]});
}
}

openElement('object',
isIE ?
{
classid: "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93",
style: "border-width:0;",
codebase: "http://java.sun.com/products/plugin/autodl/jinstall-1_4_1-windows-i586.cab#version=1,4,1",
name: JSTOOLS_ID,
id: JSTOOLS_ID
} :
{
type: "application/x-java-applet;version=1.4.1",
name: JSTOOLS_ID,
id: JSTOOLS_ID
});

addParameters({
archive: (location ? (location + '/') : '') + 'jstools.jar',
code: "com.google.javascript.keyboard.JsTools",
mayscript: "yes",
scriptable: "true",
name: "jsapplet"});

closeElement('object');

return {
sleep: function sleep(n) {
document[JSTOOLS_ID].sleep(n);
}
};
};


This is kind of sad that I have to run it before the document ocntent is finalized; there must be a way to use DOM to build the applet element when it is needed - but the straightforward solution does not seem to be working, and what the heck, it is a unittest, where, according to josh kerievsky, everything is allowed.

There's a tricky parameter that one has to pass to tools(): applet location; we may later decide to store it somewhere else, outside our main directory. So, in my unittests, I instantiate my JS_TOOLS variable like this:

var JS_TOOLS = jstools("tools"); // the jar is in tools directory


Well, that's it. The script loads, and the variable instantiates.

Someone would complain about 0.3 second delay in running the test, right? Not clean, not "small", and flaky. Okay, suggest something else if you are really worried about .3 second delay in getting your test results.

P.S. You can try it here.

Tuesday, August 05, 2008

Java Style. Iterating over collections

Suppose you decide to concatenate collections; it should of course look something like this:


Collection<A> cat(Collection<A>... collections);


What you need to do is return an instance of anonymous class extending AbstractCollection<A>; and you will need to implement two methods, iterator() and size().


Collection<A> cat(final Collection<A>... collections) {
return new AbstractCollection<A>() {
public Iterator<A> iterator() {
...
}

public int size() {
...
}
};
}


Implementing size() is almost trivial: we can start with this naive code


public int size() {
int size = 0;
for (Collection<A> c : collections) {
size += c.length;
}
return size;
}


Now raise your hAnds who think this is the correct solution... Wrong.

The problem is that for a large collection the result may be not precise. Quoting Collectio.size javadoc: "Returns the number of elements in this collection. If this collection contains more than Integer.MAX_VALUE elements, returns Integer.MAX_VALUE."

So, let's rewrite size():

public int size() {
int size = 0;
for (Collection<A> c : collections) {
if (c.size() == Integer.MAX_VALUE) return Integer.MAX_VALUE;
size += c.size();
if (size < 0) return Integer.MAX_VALUE;
}

return size;
}


If you find an error in the code above, let me know.

Now, iterator(). Not that I doubt you can implement it; what I want to say is that in this exercise minute details turn out to be important.

Here's our first attempt:


Collection<A> cat(final Collection<A>... collections) {
return new AbstractCollection<A>() {
public Iterator<A> iterator() {
private int i = -1;
private Iterator<A> current = null;


public boolean hasNext() {
while (i < collections.size()) {
if (current != null && current.hasNext()) return true;
current = collections[++i].next().iterator();
}
return false;
}

public A next() {
if (hasNext()) return current.next();
throw new NoSuchElementException();
}

public void remove() {
if (current != null) current.remove();
}
}
...
}
}




Here we are trying to be smart, bypassing empty collections, and getting to the one that has something; this will also work if the argument list is empty.

The problem is, it won't work if the argument list is not empty: see we are doing this assignment,

current = collections[++i].next().iterator();
- when i reaches collections.length - 1, we will go overboard. So we have to fix this, replacing the line with

if (i < collections.length) current = collections[++i].next().iterator();


This does look disgusting! In addition to checking for null, we are checking the same condition twice!

Null... can we get rid of null? Let's try this: set the initial value of current to an empty iterator:

Iterator<A> current = Collections.EMPTY_SET.iterator();


So that now we can say just

if (current.hasNext()) return true;

instead of

if (current != null && current.hasNext()) return true;


Good, good. How about index checking?

I have this idea: replace an array with a list (actually, an iterator). We take the array of arguments and turn it into a list, like this:

public Iterator<A> iterator() {
return new Iterator<A>() {
// Top iterator
private Iterator<Collection<A>> i = Arrays.asList(collections).iterator();
// Bottom iterator
private Iterator<A> j = Collections.EMPTY_SET.iterator();

public boolean hasNext() {
for (;!j.hasNext() && i.hasNext(); j = i.next().iterator());
return j.hasNext();
}

public A next() {
if (hasNext()) return j.next();
throw new NoSuchElementException();
}

public void remove() {
j.remove();
}
};
}


Amazing, heh? Where's double index checking? It's gone. It's all hidden inside the delegate iterator.
That's final.

Thursday, July 24, 2008

русская клавиатура для вашего браузера

Перетащите вот этот линк: Рус туда, где у вас в браузере сидят закладки (bookmark bar). И потом, когда вам надо будет что-нибудь написать по-русски, скажем, в ЖЖ, просто кликните на эту закладку, и бабац - вот вам клавиатура.

Oh, and here's an Israeli version:

Kbd

Tuesday, July 24, 2007

internet is half dead

From valleywag.com:

365 Main, a datacenter on the edge of San Francisco's Financial District, is popular with Soma startups for its proximity and its state-of-the-art facilities. Or it used to be, anyway, until a power outage took down sites including Craigslist, Six Apart's TypePad and LiveJournal blogging sites, local listings site Yelp, and blog search engine Technorati. The cause? You won't believe it.

A source close to the company says:

Someone came in shitfaced drunk, got angry, went berserk, and fucked up a lot of stuff. There's an outage on 40 or so racks at minimum.

Whoever it is, while we like how you roll in theory, in practice, we'd appreciate it if you laid off the servers running websites we actually use.

We're sure 365 Main will deny that such a thing could ever happen. And, conveniently, the neighborhood is having power troubles, too. But here's a question: When you have several levels of redundant power, what could bring your customers' servers down other than something like an employee physically ripping the plugs out of the wall?

==========================

Now, add to this that netflix has been dead for over 24 hours now. An amazing day!

Friday, June 02, 2006

Taming The Wildcard Generics

Have you ever tried to work with List<? extends Number>? You get this list from some smart program, and then try to do something with this collection. Only to find out that you cannot add anything to it. Why cannot you? You open books, you look up on Google, you go to java.sun.com, and finally you figure out that it is not a “generics bug”, but a regular, normal behavior. Here is the reason why:

Suppose some method produced an ArrayList<Integer>, and returns it disguised as List<? extends Number> - there is nothing wrong with it. But then the receiver, knowing only that it is a List of something extending Number, tries to add a Number to the list, namely, a Double – can it be legal? Of course not, because the original container expects only Integers, You cannot legally do it – some other method still expects this to be a List of Integers.

When the code is executed, all the generics information is erased, so that this kind of checking is done during compilation time. And during compilation time there seems to be no way, in the code that receives List<? extends Number>, to figure out what is the actual type of elements inside. We just do not know; all we know is that if you extract an element, you will get a superclass of Number. That’s why it is impossible to allow to add anything at all to such a list.

Interesting, right? One can easily remove elements from such a collection, but cannot add anything. You are not allowed, for List<? extends T> list to do list.add(T t) – but it is okay to remove elements from the list. The same is true for a Collection<T>, for a Map<K,V>.

I had a slightly different problem to solve: having a collection of collections, produce a virtual collection that lists all the component collections.

What does it mean that the collection is virtual? It means that no collection object exists – it may be a view into some other collection, or a class that behaves as if it contains elements, while actually it does not. Take a look at Collections.synchronizedList() – it returns a synchronized (thread-safe) list backed by the specified list. No significant amount of additional memory is allocated when you wrap a list using this method; and if the original list changes, the synchronized list will change accordingly – and vice versa.

Note that this kind of wrapper does not in any way influence the objects contained in the list – they are the same objects; the changes are in the methods that we use to access them.


The Problem



So, this is what I wanted to accomplish: having a Collection<? extends Collection<? extends T>>, produce a virtual Collection<T> - a collection that would change when components are changing, from which we could remove elements and to which we could add elements.

The desired collection should contain all the elements from the component collections; I want to be able to remove the elements (meaning, they are removed from components); I want to find a way to add elements to my resulting collection.

Note that this is not a flat collection produced from a tree represented as a collection of collections of collections, etc. Flattening happens on just one level: we had a collection of collections of type T, and produce a collection of type T.

One more natural requirement is that, when I list the elements of the resulting collection, they will be listed in a natural order: first, elements of the first component are listed, as if we were scanning the component itself, then the second one, etc., till the last element of the last component.

The functional difference between an Iterable<T> and a Collection<T> is essentially that with a Collection one can get its size and add elements. Of course, to calculate the size of a compound collection one has to add up the sizes of its components. Yes, the size can be changing while we count – but the same can happen to an ordinary collection as well, so we should not be too surprised.

A Collection<T> has actually two addition methods: add(T element) and addAll(Collection<? extends T>). Remember, the intention is to create a virtual, live collection, which means that we are not interested in making a copy of the collection passed to addAll() method. We want to use the collection itself.

But when we add an individual element, T element, we cannot add it to any original component.

It is easy to introduce a compound collection class that can shrink but cannot grow; let’s call it ShrinkingCompoundCollection. It works as described above, by scanning through individual components; deletion as I said above, is fine, but addition cannot be implemented..

The Solution



Let’s denote the original upper level collection of collections as A, and its components as Ai. Since we cannot touch neither any of Ai, nor the original collection A, we have to have two “overflow” collections. One, upper level, collection will receive all the collections that are being added using addAll() method. Let’s denote this upper level collection as B, and its components as Bk. When listing elements of the whole compound collection or calculating the size of the compound collection, we have to scan through all the Ai, and then through all the Bj.

The first component of B, Collection<T> B0, has to be added when B is created. This component will accept all individual elements added by calling add(T element).

So, we have the following structure: a collection C consisting of A and B; each one consists of “elementary” collections with elements of type either T or ? extends T. Now, have not you noticed that we have tree levels, instead of two. How do we handle this? By iteration. Let’s denote as F the operation of removing one layer of indirection, that is, the conversion of Collection<? extends Collection<? extends T>> to Collection<T>. Applying this operation to C, which is a Collection<Collection<? extends Collection<? extends T>>>, we get a Collection<Collection<? extends T>>; applying F to the resulting collection, we get the required Collection<T>.

How do we do it, if we are supposed to use a constructor? I never heard of recursive constructors yet. Well, actually we do not need much of recursion.

Watch my hands. Having a Collection<? extends Collection<? extends T>> A, and having created an “overflow” LinkedList<? extends Collection<? extends T>>, produce a collection out of these two, and pass it to a ShrinkingCollection constructor. The constructor returns a new Collection<? extends Collection<? extends T>>, the first element of which we know, it is our LinkedList.B. Add to it an “individual overflow list”, LinkedList<T> B0, and pass the resulting parameter again to super.constructor. We have built a Collection<T>, which is an instance of ShrinkingCompoundCollection<T>, and we know how to add to it collections and individual instances of T.

Q.E.D.

Две Черные Ласты

У нас в Архангельске вообще практически никто ни с кем не дрался. Я прожил там 15 лет, и не было случая, чтобы с кем-то там драться, чтобы кто-то напал. Ну разве с соседкой по парте Ниной Киселёвой, так и то – мы не друг друга мутузили, а она бросала на пол и топтала моё пальто, а я – её шапку. Чисто символически выражали свои чувства. Нет; один раз я присутствовал при драке – Лёнька Карельский с Серёгой Гермашевым поссорились из-за Сталина. Простак Лёнька сказал, что если бы Ленин был жив, то не дал бы Сталину устраивать репрессии; Серёга на это, цинично усмехнувшись, процедил, что Ленин и Сталин – едины; мы говорим Ленин – подразумеваем партия, говорим Сталин – подразумеваем тоже партия. Хоть у меня по русскому была вечная тройка, но меня слегонца бесило, когда Серёга нарочно вот так вот коверкал язык. Но не настолько меня бесило, чтобы из-за русского языка ещё драться – хватало мне дуры училки Валентины Александровны, которая даже не верила, что у Маяковского есть такое слово «земшар» - а я-то Маяковского уж всяко получше её знал. Да и ни из-за чего я не стал бы драться... ну разве если бы придурок Бармин вдруг обидел бы мою одноклассницу Валю К, так я бы вязл кирпич и убил бы его тут же, на это я был морально готов. А так драться – нет, не в моём вкусе.

А Лёнька с Серёгой как сцепились из-за Сталина, так и валялись, пыхтя и ругаясь, по школьному двору, пока совсем уж не стемнело. Я сидел на корточках и лениво уговаривал их: «кончайте вы, ребя», но кто ж в таких случаях прислушивается к голосу разума. Голос желудка – другое дело. Лёнька оголодал, вырвался из объятий Серёги, и полез через забор домой (Лёнька через забор от школы жил, так его дорога в школу и из школы в этом и состояла, в перелезании забора. А сразу за школьным забором стоял Лёнькин сарай, и в этом сарае, кстати, лежало полное собрание сочинений Сталина; мы эту гадость потом вместе с Лёнькой в школу на макулатуру стаскали – конечно, перекидывая тоже через забор.

Лёнька полез на забор, а мы с Серёгой двинулись в сторону наших домов, и Лёнька, сидя на заборе, обозвал меня предателем, потому что если я против Сталина, то не должен я с Серёгой ходить. Как будто Гермашев был за Сталина: ему просто смешна была наша глупость. Плюс, горячий астраханский характер. Серёга из Астрахани был, чердак называл «подловкой» и т.д. И своего младшего брата Вовку он иной раз бил так, что я уже был готов милицию вызывать. Не любил я Серёгу Гермашева за эти вот жестокость и цинизм; так и раздружились. В девятом классе, увидев его на остановке автобуса, я отвернулся – как и он от меня. А больше встретиться и не довелось – его застрелили через пару лет в Соломбале. Спасал кого-то от хулиганов, его и застрелили.

И кроме этой драки, ничего и не было. Мы во дворе жили себе своей жизнью, и никто никого пальцем не тронул. Люди были разного достатка – моряки, ходившие в загранку и привозившие диковинную одёжку для своих детей – а также чуингам, который, по словам наших учительниц, был вреден для организма и вызывал непрерывное слюноотделение. Жил шофёр Фёдоров с двумя глупыми дочками, погодками. Сёстры Фёдоровы любили верещать, когда их пугают, и в наши игры, «десять палочек» и «ваш чин», играть не любили. Были ещё у нас во дворе пенсионеры, один даже парализованный. Парализованный – но умный, научил нас всех делать воздушных змеев. Ну и наша вот семья тоже, не пришей не пристегни, не в квартире, как все остальные, а в отдельном домишке без газа и водопровода.

Нас соседи считали за очень бедных, и ко мне относились сочувственно. А я этой разныцы не ощущал; что с того, что у тебя телевизор дома, от этого умнее, что ли, станешь. Нет, я этого не ощущал; и городская библиотека была доступна всем, и еды тоже хватало в общем-то; хоть я, хоть Мишка Сорванов, хоть Колька Афонин - выходили иной раз на улицу с одинаковым бутербродом в виде посоленного куска свежего черного хлеба, и делились этой едой с налетавшими окружающими.

Вот этой дворовой кодлой иы и ходили летом на пляж, а иной раз и бегом бегали. В Архангельске в мои времена было три пляжа. Большой городской, тянулся километров на пять, от улицы Энгельса до яхтклуба, широкий, с хорошим песком; на нём в жаркую погоды толпился народ. Пляж с бонами, до которых в прилив было доплысть не сразу, а в отлив можно было пешком дойти. С бонов хорошо нырять. Если ловкий, то можно и на столб забраться, к которым эти боны привязаны на тросах, и со столба, с высоты метра полтора, нырять в Двину. В Двине вода летом тёплая, и течение не сильное в районе городского пляжа. Но вода мутная, видимость примерно метр; до дна надо донырнуть ещё, чтобы что-нибудь там увидеть. Особое развлечение – нырнуть с бонов и проплыть под водой до такого места, где стоять уже можно, где мелко. Кстати, срать в воду разрешалось только за бонами. Там – река унесёт. Вот ещё один повод сплавать до бонов.

Так как летом вода тёплая, то мы были избалованы, и в холодной воде купаться не тянуло. Раз только один, по инициативе шебутного Мишки Сорванова сходили в конце мая на городской пляж, искупаться среди плывущих льдин. Нет, не люблю. Кости ломит; яйца как доской прищемило. Кому нужно такое удовольствие?

Второй пляж был на месте строящегося речного вокзала, Речнухи. На Речнухе стояли горы привозного песка, настолько огромные, что зимой мы туда ходили кататься на лыжах, устраивали трамплины внизу... даже на уроках физкультуры нас туда гоняли, когда проходили катание с гор. Лыжи были главным предметом на физре, понятное дело.

На Речнухе, если честно признаться, пляжа-то никакого не было; был просто песок, была река, валялись вынесенные рекой брёвна, и выброшенные кем-то шины. То и то нам годилось – на бревне можно было прокатиться по реке, пытаясь удержаться на нём сидя; шины жгли на берегу, для жару, чтоб согреться, потому что на Речнуху ходили уже когда довольно холодно было. Дикий был пляж, эта Речнуха.

Ещё один пляж, оборудованный, но очень маленький, был у моста через Двину. Боны, выгороженная зона для плавания, вышка для ныряния. Здесь берег был очень крутой, и течение быстрое, так как место узкое. Самое узкое место Двины – восемьсот метров всего. Полмили.

Вот мы и пошли на этот пляж всей кодлой – Валерка Колодкин, Мишка Сорванов, Колька Афонин, Валерка Ожогин и его старший брат Сашка, а также Сашка Зуев. Такое количество Валерок я объяснить не в состоянии, а вот аномально высокая концентрация Сашек в первой волне бумеров объясняется просто – популярностью фильма «Александр Невский». Гордый профиль артиста Николая Черкасова, его арийская челюсть, плащ с кровавым подбоем, стальная будённовка вдохновляли вернувшихся с войны Отцов, мол, и их Сашка тоже вырастет таким. Победителем. Ожидания такого рода не всегда сбываются; некоторые из Сашек, выросши, вдруг стали Шуриками. Отцы не виноваты, виновата эпоха.

Валерка Ожогин, как и Мишка Сорванов, на год младше меня. Мне тогда нравилось это весёлое, незамороченное поколение – не то что Колька Афонин, на год старше; у Кольки в голове бродили очень странные для тех мест и того времени мысли... нынче бы – нормально, ну гомик и гомик, чё такого-то. Валерка Ожогин был мордаст и черноволос; у его брата Сашки тоже черные волосы, зачесаны гладко назад; Сашка худ, мускулист. Сашка был стилягой – только причёска не стиляжья. Иной раз, в субботу вечером, можно было его встретить в узких брючатах, с галстуком-верёвкой на пестрой «гавайской» рубахе. Про такой галстук моя тётя Лиза говорила, мол так бы и удавила этих стиляг на этих их галстуках. Не то чтобы тётя Лиза служила в обществе лимфоцитом, нет, она работала электриком на ТЭЦ – но она была передовик труда и искренне разделяла взгляды выписываемых ею журналов «Крокодил» и «Огонёк». Нет, до вступления в партию ей было далеко – во-первых, она была твёрдая христианка, во-вторых, родственники были слишком антисоветские - как соберутся два дедушки, отец и свёкор, да да как начнут «провергать коммунистов»... хоть и смеялись над дедушками, но всё ж уважали.

От Сашки Валерка Ожогин подхватывал модные иностранные песни, «рок, раунд зе клок», «твист энд шаут», и «абессамэ, абессамэ мучо» - на загадочном испанском, через много лет вдруг ставшем мне почти родным. Английский в наших местах хоть тоже был экзотикой, но встречался чаще; Лёнька Карельский ещё в третьем классе сообщил нам, что по-английски «кто отсутствует» будет «хуйзэпсэн» - и мы ждали, ухмыляясь, когда дорастём до пятого класса и услышим это своими ушами от Маргариты Иосифовны.

Да Сашку Ожогина, может быть, кто-то даже и побаивался, типа возьмёт да зарежет – такой стиляга-то! Колькина мать многое за ним подозревала. Она и за мной подозревала, что я губы крашу. Ничего семейка была. Подозревать двенадцатилетнего парня в сапогах и сером ватнике, что он губы красит!

Кодлой, конечно, весело купаться – по очереди с вышки ныряли; проплывали под водой (по течению) весь «бассейн»; ныряли по двое, под водой пугая друг друга всякими страшными жестами и рожами... У Сашки Ожогина были ласты, и он в них заныривал глубоко, до дна. У меня была маска, а ластов не было (да шутка ли, ласты стоили 7 рублей 10 копеек в магазине, чуть дешевле моего фотоаппарата, на который я два года копил, экономя на пирожках.

И вот, прыгнув с вышки в моей маске и в сашкиных ластах, я вынырнул в маске и одной ласте. Другой не было. Как бы это вам объяснить, в масштабе... Ну примерно как если у вас угнали незастрахованную машину, которую вы у приятеля взяли прокатиться.

Остаток дня я провёл, ныряя в своей маске в этот «бассейн». Не каждый раз удавалось донырнуть до дна - не хватало дыхания. Глубоко там всё-таки, метров пять; да и холодно на глубине. И темно. И эта мутная вода, которая тебя несёт над дном, пока ты по нему шаришь, пытаясь найти хоть какую-то неровность или что-нибудь тёмное... Наконец Сашка мне сказал «хватит», чтобы я перестал, что её всё равно уже не найдёшь.

Дома рассказал матери... мать дала денег, и я пошел к Ожогиным и пытался вручить им семь рублей. Зажиточная квартира, интеллигентные люди. Родителей Ожогиных я видел первый раз, и довольно-таки стеснялся. Они, конечно, никаких денег не взяли у меня – да и кто бы на их месте взял?

Это чувство, чувство отсутствущей ласты на правой ноге, чувство отчаяния, с которым ты шаришь руками по дну, в полумраке, а мутная вода сносит тебя вниз по течению, и ты пытаешься вернуться... эта густая холодая вода над серым дном, за которое я пытаюсь уцепиться, эта вода мне всё ещё снится – и я думаю, а куда же вот делась эта ласта? Куда?


Вторая ласта пропала 26 лет спустя. Моему сыну было столько, сколько мне тогда на пляже у моста.

Мы болтались по Вуоксе большой туснёй – Кротовы с детьми, Пихтины, Шура Холоимов. Наконец удалось нам добраться до моей заветной стояночки на Холмистом, на скале, где мы с Андрюшкой Кротовым даже выдолбили шлямбуром в скале канавку, чтобы вода не хастаивалась у камня, за которым мы пили чай и играли в ап-энд-даун, а стекала, и место чтобы оставалось сухим, только мох. Мы там вообще пообосновались; я даже примеривался сажать картошку и укроп на лугу на острове Фиалка, это напротив Холмистого.

В июле вода в Вуоксе теплющая; так что мы только и делали что купались. Дети ещё пытались ловить какую-то рыбу, чтоб кормить нашу знакомую чайку Чуку, но в жару рыба не особо спешит на крючок – а если попробовать рано утром, в пять часов – так это ж слишком холодно, и дураков нет вылезать из палатки мёрзнуть над неподвижной водой... так что пути наши и рыбьи не пересекались. У детей любимое место для рыбалки было – скала напротив, небольшая скала, высотой метра два – на ней можно сидеть рыбу ловить, анекдоты травить, а можно нырять, только надо знать куда, чтобы в ил не запилиться с головой: ил в нашей заводи знатный.

Дети вздумали гонять вплавь на время до скалы и обратно. К детям присоединились взрослые, начиная с Кротова. Я-то стоял на берегу и усмехался – никогда я не умел быстро плавать; я предпочитаю плыть не спеша, будь это хоть час, хоть два. А Шура – парень спортивный, и тут же присоединился к Кротову. Шура нас всех лет на десять этак моложе был. Худой, жилистый, небольшого роста, круглое лицо, весёлые карие глаза, глубокое понимание жизни и всех её поверхностных вещей; и с иронической улыбочкой ташит хоть какой тяжести рюкзак. Меня пробовал учить плавать кролем, да меня учить физкультуре – примерно как учителя физкультуры языку Лисп. Так что я стоял на бережку и засекал по часам время, рекорд 54 секунды в один конец.

Потом Шура решил ешё смерять, ну а сколько же будет если с ластами плыть; взял надел мои ласты и поплыл. И бац – свалилась с его маленькой ноги моя большая ласта, аккурат посредине между берегом и скалой. Взял я маску, стали мы с Шурой по очереди нырять. Да чего там нырять – хоть видимость и есть кое-какая, но на глубине два метра упираешься в слизистый жидкий ил, который при любом прикосновении вздымается облаком; и сколько там этого ила – неизвестно. Ну да и вода в Вуоксе только на поверхности тёплая, а там, ближе к дну – ледянющая. И ничего не нашли, конечно. Отметили только это место, мол, вот, здесь покоится чёрная ласта.

И в следующие заезды я своим гостям место показывал: так и так, мол, здесь Шура ласту утопил. А вот здесь чайка Чука жила. А здесь к нам однажды вышел беглый солдат с большим ножом и попросил перевезти на тот берег. А здесь Ник с Фаей лося как-то утром встретили. А у этого нашего камня снимались «Особенности». А здесь раньше был фарватер, теплоход ходил. А здесь когда-то избушка стояла, и в ней жил парень, и этот парень угощал нас чаем из чаги. А вот хутор Пёйлякаллио, и сосна, которую тихуанский колдун Ч назвал дубом, и намерял у этого дуба биополе - 8 метров в диаметре.

Через восемь лет Шура Холоимов умер от рака.

Ну это нам так легко говорить, мол, взял да умер. Это вот если ты, скажем, едешь по 85-й дороге в левом ряду, включил радио Пуро Мехико, да орёшь вместе с ними: Besame, besame mucho, como si fuera esta noche la ultima vez… oh shit! – и всё. Твои последние слова были «оу щит». Всё очень просто – для тебя. А с раком непросто. Сначала же надо вернуться от доктора, набраться сил, и твёрдым голосом, слегка, правда, задыхаясь, сказать: «мама, у меня рак.» Потом операция. Потом химия. Потом, после химии, облучение, рвота, облысение, тоска и бессилие. Если повезёт – без болей, а не повезёт – боли, с каждым днём всё неутолимей.

Потом койка.

Лежишь, глядишь в белый потолок.

Ждёшь.

Saturday, January 14, 2006

Ныряя Глубоко - 1

Мой родной дом... мне невероятно повезло в жизни - я провёл детство в отдельном доме. Ни соседей сверху, ни соседей снизу, ни слева, ни справа. А так как бабушка с дедушкой были довольно глухие, то я привык вытворять всё, что мне в голову взбредёт, уже в семь лет вставляя жучки в пробки, перегоравшие из-за моей любви к электричеству...

Пятистенка, построена в начале 20-го века; серые стены, общая площадь где-то метров тридцать пять - сорок.

Материна комната занимала полдома. Окнами на юг, на восток и на запад. Моя выкрашенная светло-голубой краской кроватка стояла у восточного окна. Пока не сняли решетку, я, просыпаясь ночью, вставал к окну и глядел на золотисто-голубое небо архангельской белой ночи. Ещё не были выстроены ни двухэтажные брусчатые дома через дорогу, ни высокие, с антресолями, дровяные сараи к этим домам - и открывался вид ну почти что на тундру - в июне пушок, потом иван-чай... Метрах в ста стоял старый двухэтажный дом на сваях; этот дом таинственная сила вымораживания выталкивала из земли и поднимала всё выше, так что мы с матерью, идя в гости к дальним родственникам Вяткиным (взяв с собой в качестве гостинца кулёк с желтыми конфетами), проходили, как и все, прямо под домом, чтобы потом подняться на находящееся с обратной стороны крыльцо.

Однажды, холодным летом 53-го, часов около пяти утра, этот дом вдруг со страшным грохотом просел, так что стены встали на землю, а пол первого этажа оказался на расстоянии меньше метра от потолка. В те времена было принято спать в железных кроватях с железными спинками с шариками, и вот эти спинки с шариками, эти крепкие стальные трубы и спасли людей - никто не погиб и не был даже ранен, все как-то выползли, как выросшая Алиса, из ставшего маленьким домика.

Облако пыли разлетелось в стороны, и медленно осело на землю.

А так, из моей стратегически расположенной кроватки, если идти по часовой стрелке с севера, были видны следующие вещи и предметы:

- Светлый фанерный гардероб, с двумя ящиками старой обуви, с вешалкой, с кусочком нафталина в целлофановом пакетике, с портупеями, ремнями, сумками... Что было на крыше того гардероба, мне было совершенно не видно.

- Голубые в широкую светло-пурпурную "цветочку" обои, отстающие по верху и по низу, несмотря на то, что по верху был проложен широкий бордюр. Это была отдельная работа - резать ножницами на полоски рулон бордюра, перед поклейкой.

- Затем уже упомянутое выше окно.

- Затем выкрашенная голубой краской тумбочка, на которой стояла огроменная эмалированная кастрюля (очевидно, списанная из детского сада, где работала мать), в которой рос огромный же, почти до потолка, фикус.

- Прямо над фикусом - мраморный щиток, на котором электрический счетчик и две пробки; меня гипнотизировало жужжание счетчика, неугомонное вращение горизонтального колёсика с меткой, и, время от времени, тиканье цифр. Туго скрученные провода идут под потолком от щитка на кухню, к выключателю, к лампе на потолке... провода примотаны стальной проволокой к маленьким изящным фарфоровым катушкам, то ввинченным в стены ржавыми шурупами, то прибитыми на толстый гвоздь. 127 вольт было тогда бытовое напряжение; уже под конец царствования Хрущёва нам поменяли напряжение на 220.

Справа от электросчётчика висит портрет великомученицы Екатерины, чей духовный подвиг давал моей матери какие-то силы выносить все эти тяготы, что выпали на её долю - мне иногда кажется, что всю удачу, которая могла бы достаться на её долю при честном распределении, она оставила, сама того не ведая, мне, баловню, которого она родила в сорок лет, чтобы не оставаться одной... ну это потом, сейчас мы просто обводим любопытным взором, с высоты кроватки, мою вселенную, как я её впервые увидел.

- Справа от фикуса ещё одно окно, на юг. За ним - стройка, строят двухэтажный брусчатый дом, куда потом въедут наши будущие соседи. Афонины, Сорвановы, Колодкины; капитан Иван Севастьянович, чья взрослая дочка была первой шахматисткой города Архангельска; она же научила меня привезённым с Кавказа песням только что вылупившегося Визбора: "а друг рисует горы, далёкие, как сон..."... не будем спешить, это всё в будущем, соседи, капитан... всего в той стене три окна, это фасад дома. На зиму окна покрываются сначала инеем, а потом и сантиметровым слоем льда, и мне не разрешают протапливать дырку, так как от неравномерности нагрева стекло может запросто треснуть... но и это потом.

- Ещё одна табуретка, на которой стоит желтый патефон; пластинки в пожелтевших бумажных конвертах на полу рядом, прислонены к табуретке. Апрелевский Завод Грампластинок. Русланова, "Собака на Сене" Лопе де Вега, "12-я ночь" Шекспира. Я лучше патефон сейчас пропущу; это отдельная большая тема, с его иголками, головками, регуляторами, большой пружиной, внутренним рупором, и т.д.

- Затем комод; комод выглядит свежее, и сверху на нём лежит кружевная скатерть (живя в Архангельске, мать не плела больше вологодские кружева, хотя и умела, и подушка с коклюшками, а также и трафареты для кружев, пылились на чердаке. На скатерти стоят зеркало побольше и зеркало поменьше, а также две розовые гетинаксовые шкатулки с разной ерундой. Но до ерунды дотянуться ещё надо - залезть на табуретку, например. Ну и будильник, большой, бежевый, с блестящим никелированным звонком сверху, внутри которого цилиндрический молоточек на толсткой стальной проволочке.

Над комодом висят два портрета, Лёнюшка, мой брат, которого я никогда не видел, и смерти которого я обязан своей жизнью: если б Лёнюшка не погиб, четырнадцати лет, мать не стала бы заводить себе на старости лет ещё одного ребёнка... а рядом с Лёнюшкой - Лёша, мой дядя, в военной форме.

Лёша выстрелил себе в лоб, будучи в карауле. Служил он под Мурманском, в Североморске, и однажды полярной ночью не выдержал этого всего. Лёша всегда был одиночкой; он был слишком мягок, слишком честен и прост, как и все, кому посчастливилось вырасти под руководством моей бабушки. Плюс маленький рост - сволочные мурманские девки, с золотым зубом, со взбитым коком, в крепдешиновых платьях, избалованные ходившими в загранку моряками, над ним ну просто смеялись.

Ему и с самоубийством не очень повезло - не умер сразу же, а в недоумении встал, пошел к двери караульного помещения, и уже у двери рухнул навзничь.

Военное начальство в ту послевоенную пору и в тех неавантажных местах было благородным; они списали на несчастный случай, и поэтому выписали бабушке с дедушкой пенсию, 350 рублей, которой им вполне хватало... ещё одна смерть, сделавшая возможным моё существование. Лёшины записные книжки я одно время почти наизусть помнил.

На портрете Лёше 26 лет, и в моих глазах, с течением времени, он из пожилого дяденьки превращался в почти ровесника, молодого офицера, а потом и в несчастного мальчика, которого мне до сих пор жалко.

За комодом стоит табуретка, а за табуреткой стоит трюмо с мутным старым зеркалом; трюмо тоже мутноватое, сосна, покрытая морилкой и лаком, потёртое, старое. Над трюмо висит большой черный плоский конус: Репродуктор, радиоточка. У Репродуктора в середине расположен трансформатор и катушка; есть также маленькая ручка, позволяющая регулировать громкость. Так как Репродуктор висит под самым потолком, то и достают до него только взрослые, а я лишь слушаю. Песни Руслановой, Последние Известия, Архангельские Новости ("стивидоры Соломбалы досрочно завершили.."). Репродуктор учил меня Правильному Русскому Языку. Это благодаря ему я перестал говорить "радево" (радио), "пошто" (почему), употреблять послелоги "-то", "-та", "-ту", "-те".

Тут мы уже подходим к западной стенке, у которой стоит материна кровать. Большая железная кровать, шарики на спинках (половины нету - часть, похоже, откручена ещё до меня). На железной сетке положен пружинный матрас, а на нём толстый пуховый матрас. Темно-розовые подушки с рисунком "огурец"; темно-синее одеяло всё в тот же "огурец". Попасть туда ночевать - это надо было или хорошо себя вести или заболеть. Я чаще практиковал второе. Лежишь, водя глазами по потолку; болезнь накатывается на тебя как тяжелая густая волна какого-то сиропа; всё становится вдруг медленным и тёмно-медовым... и потом эта волна откатывает, и становишься лёгким, пустым, кажется, всплывёшь вместе с одеялом к белёному потолку в загадочных, полных скрытого смысла и тайных букв трещинах - но мокрая, вспотевшая голова липнет к подушке, и не оторвать. Попросишь "пить", и мать спешит с большой чашкой с золотым ободочком; в чашке чай с молоком, с маслом, с пенкой, с мёдом: "пей!". Я ничего не имею против пенки, но в больное горло она не лезет.

А в хорошие времена залезешь бывалоча на спинку кровати и валишься плашмя на матрас, аж подпрыгиваешь потом. Страшно, но здорово.

У кровати висит густой короткошерстный красный ковёр, очень чесучий, если к нему прислониться. Лучше под одеялом. Конечно, хорошо, когда разрешают там спать. Можно разговаривать, спрашивать про всё, или попросить сказку рассказать - да только она никаких сказок не знает, я сам лучше почитаю. А вот рассказать из жизни что-нибудь, как она в школу ходила, как учитель иной раз придёт утром и скажет: "ребята идите вы домой, я за охотой пошел" - и всё. Как после революции "боже царя храни" петь в школе перестали, а стали петь песню про берёзку. Мать училась недолго, четыре класса всего окончила. А больше и школы в деревне не было. И то неплохо; бабушке пришлось самой выучиться грамоте, чтобы потом Толстого да Гоголя читать. Дедушка же, хоть и служил в Финляндии, а даже расписываться не особо-то умел, норовил всё крестик поставить. Книги для него были - ничто. Пустое место. На цигарку да подтереться. Ну примерно вот как вам - AJAX-технология или интуиционистская семантика Крипке-Жуаяля.

Sunday, December 18, 2005

Мой начальник Мыкола Гомец

Date: Tue, 04 Apr 2000 18:24:25

У меня есть начальник, Мыкола Гомец. Он по-испански, правда, не очень, его родной язык швед­ский. Ну и еще сантакрузский – "beer dudette", скажем. И мы с ним карпулились. И вот он спра­ши­ва­ет у меня, как по-русски будет "shit!" Ну, подумав, сказал – "бля!". И вот заходит Мыкола вечером в кафе в Санта Крузе, видит Лену, тыкает в нее пальцем и орет: "бля!". Ну лааадно... через не­делю спра­шивает у меня Мыкола, как по-русски с днем рождения поздравляют. Смотря как. Не­фор­маль­но. Смотря какого пола. Девочку. "Расти большая". Едем-едем. А как, спрашивает, спросить "how's your nose?". "Как твой нос?", отвечаю. И вот он приходит от вечером в кафе в Санта Крузе, под­хо­дит к Лене и, в присутствии ее родителей, говорит ей торжественно: "расти большая, как твой нос!".

Больше я его русскому не учил, выучил он только имя свое – Мыкола, да и то Лена ему впаривает, что это означает, что он redneck – а я говорю, не реднек, не реднек, просто Украина и Швеция – близ­нецы-братья, недаром Мазепа пытался оную к оной присоединить, у них и флаги у обоих жов­то­-блакитные (бло-гюль, по-шведски), и фразу "хай, добро, так" швед поймет примерно адекватно. Мо­жете попробовать, это приличная фраза.

Понедельник

Date: Mon, 15 May 2000 18:36:53

Да неделя как неделя была, токмо что жара и засуха до октября, которые мы с Бредом Жилетом так друж­но обещали швейцарцу Урсу Шпюхеру, сменились густыми облаками и проливными дождями. От Миши "эль ниньо" ничего, однако, не слышно. Непонятно.

В пятницу, в связи с производственным визитом в город Сан Матео, зашел в родную школу, по­здо­ро­ваться с учителем Василием Ивановичем. В классе было полно народу, все заорали, увидев меня, и стали дружно поправлять мой английский – мой английский был любимчиком этого класса. Дат­чан­ка Лада обняла меня и чуть не прослезилась. Радостно орала, увидев меня, мексиканка Арасели; она на днях получила гринкарду и стала работать в аэропорту, смена с трех тридцати утра, она даже не знает, какую ей зарплату положили, по знакомству устроили; устраивается на работу и Хайм, ко­лум­биец, через две недели он будет в Оракле, что-то в маркетинге, солидный паренек. Загадочный мек­сиканец Мартин, с огромной золотой цепью на шее, улыбался молча. Чилийка Маривель при­пля­сывала за своей партой – Маривель в субботу летит к себе в Чили со своим мужем, свадьба у них бы­ла на миллениум в Лас Вегасе, сидит и напевает странную песенку – ми энд ю, ю энд ми, хомо-сек­шу-алити... Маривель недавно тоже устроилась работать – корреспонденткой в "Нуэво Мундо", ис­­панское приложение к "Сан Озе Меркури Нуз". Появились в классе две новые японки, внешне по­хо­жие скорее на тайванек. Пришел директор Тешара, который недавно вернулся из Вьетнама, и все сна­чала заорали "Алла Тешара", а потом переключились на Василия и стали привычно орать "Алла Ва­силий". Директор Тешара устроил рафл из принесенных сувениров; мне досталась голова мед­ве­дя Смоки, каковую я и прицепил к автомобильной антенне. Голова небольшая, размером с яйцо.

(Примечание. Медведь Смоки, как известно, родом со Сьерры. Уже в июне нас как-то понесло на Сьерру погуляти – и теперь нам от этой Сьерры уже и не оторваться. Голова Смоки, наверное, оказывает этакое трансцендентальное влияние.)

Потом Вася велел всем рассказывать про страшные случаи, которые с ними были. Народ-то разный соб­рался. Кого в заложники брали, к кому домой воо­ру­женные бандиты на родине забирались. Япон­ки в детст­ве тонули и с тех пор боятся воды. Колумбиец Хайм рассказал, как он служил в ар­мии, в 16 лет его за­бра­ли; уточнили, что не урожаи охраняли, а в госу­дар­ст­­венной армии служил – и там ребятишкам выдали ору­жие, и все стали целиться друг в друга, одного уби­ли, другого ранили, с тех пор они с оружием осто­рож­но. У чеха Томаша месяц назад взорвался его красный кон­вер­тибл, пря­мо как в кино – они с персом Али сели в машину, и Томаш стал заводить – и тут сначала по­лых­­ну­ло, Томаш крикнул Али – "get the fuck outa here!", они токо выскочили – и взорвалось. Менты при­ехали через минуту. Где-то за углом сторожили, видимо.

Я свою историю начал – "когда мне было пять месяцев..." – все сразу заржали. Вах, какой народ у нас в классе!

А в субботу мы с утра поехали на великах прокатиться, взяли три банана, три пряника и немножко со­ку. Проехали по парку, боринг, подъехали к лексинг­тон­с­ко­му вдхр, въехали на дамбу, ну что, объе­дем вокруг пруда? Да ну, далеко тащиться, давай домой – ну давай. Только другой дорогой по­е­дем. Ну хорошо. Дорога на Черную Гору, туда и поперлись. Часа через два (а часов у нас тоже нету, и карты нету, и денег, и телефона) подъем кончился. Питье мы берегли. И пряники берег­ли. От­ку­сы­вали по маленькому кусочку, как Зиганшин с Поплавским. Пуще всего мы берегли бананы. Во-пер­вых, после бананов тяжело ехать на велике. Во-вторых, это стратегический запас. До тридцать пя­той дороги оста­валась пара миль, когда увидели въезд в парк Сан­борн. А этот парк мы знали – то­ч­нее, знали проти­во­по­лож­ный из него выезд. Туда и попилили, хотя на вело­си­педах нельзя – да что по­делаешь, у нас имердженси, можно сказать. Места там странные, глухие – никто туда не за­бре­да­ет – и вот, глухое лесное озеро, тишь, травка, луга, как на Карельском, только деревья раз в пять по­вы­ше будут. У озера мы съели первый банан. Солнце стояло еще высоко, но и ехать нам было так, ни­чего... порядочно. Впрочем, нам повезло – если бы не парк, была бы еще пара подъемом на 2000 фу­тов, а так – все вниз, по крутой дорожке, правда, не близко это. Что странно – когда езды ос­та­ва­лось часа на полтора, появилось ощущение, что нам все вообще по фиг дым, и мы хоть двое суток мо­жем так пилить.

Вер­нулись задолго до заката, хватило сил даже дополз­ти до бассейна и джакузи, по­говорить о жилищных проб­лемах с бразилкой, сидевшей в той же джакузи – и все, и дальше мы рух­нули на диван и уже не сползали с него, смотрели всякую чушь типа Пекинской Пули. До ком­­пьютера доползти не было сил. До сейфуэя дополз­ти не было сил – ели, что есть в холодильнике.

А сегодня ливень, ливень. Хочется спать, а не работать.

И еще сегодня сорок лет, как умер мой дедушка Башаров Василий Константинович. Родился он в 1876 году. Служил в Финляндии, потом налаживал домаш­нее хозяйство, потом его посадили, по­то­му что слиш­ком хорошо работал, но на канал не послали по воз­рас­ту, и он с бабушкой жил у нас в до­ме за заборкой, и де­душ­ка время от времени собирался пойти косить сено. Де­­душка научил меня ку­рить, пить водку и мате­рить­ся (а я уже учил учеников старшей группы детс­кого сада, за что был очень популярен). Курил я, прав­да, не вды­хая – как Буш. Зато пил ничего, однажды хлоп­нул в гос­­тях стопарик водки – и бух на пол, скорую вызывать боя­лись, так оклемался. Сколько мне было... три, на­вер­­ное, или даже два. Читать еще не умел, точно. Если бы прочитал, что водка – пить не стал бы, не любил я водку. А сегодня выпью.

Перед смертью к дедушке приходил поп, исповедовать, соборовать, не знаю уж точно. Я, уб­еж­ден­ный к тому времени атеист, написал на картонке текст: "бога нет!" и, когда поп оделся и ушел, по­бе­жал за ним, и, регулярно его обгоняя, молча вставал на дороге и демон­стрировал ему мой плакат. Так мы с попом дошли до трамвайной остановки на углу Выучейского и Ломоносова, где и рас­ста­лись. Поп тоже не сказал мне ни слова.

Followers

Subscribe To My Podcast

whos.amung.us