Non-trivial constructors in Kotlin

Kotlin really simplifies things such as defining constructors and writing immutable objects for your application. For example, flexible Kotlin constructor definitions eliminate the need of builder classes (you simply don’t need them in 99% of all the possible use cases, if you use Kotlin), thus reducing overhead of having immutable objects in your application, while retaining full flexibility and expressiveness.

However, if you want to define non-trivial constructor (especially for data classes) it might not be as trivial as just writing a function.

For example, when I started playing with Kotlin, I decided to start with something as simple as defining a class that would represent a rational number.

My first brute force (or naive, if you like) attempt to define that class was as follows:

data class Ratio (val numerator : Int, val denominator : Int)

The problems with this class are obvious: you can create ratios like Ratio(1, 2) and Ratio(2, 4) and they won’t be equal to each other. And I wanted exact opposite – whenever the user of this class constructs a ratio it divides a numerator and denominator to their greatest common divisor – to have coprime numerator and denominator in the corresponding fields of the newly constructed instance of Ratio class. Also I wanted to retain nice-to-have features of the data class – I didn’t want to define copy, hashCode and equals myself.

So, at this point you’re welcome to play before reading my solution.


OK, now if you came up with your approach or just would like to see the possible solution – here is the link to the full class definition for those who interested.

In short: you can define custom class constructor (and yet retain call semantics) is to define invoke function in ‘class object’ section of your class that has special semantics: you can define static functions as well as factory function invoke. It may look as follows (simplified):

class Ratio private (val numerator : Int, val denominator : Int) {
  class object {
    val ZERO = Ratio(0, 1) // static member!
    val ONE = Ratio(1, 1) // static member!
    fun invoke(numerator : Int = 1, denominator : Int = 1) : Ratio {
      if (denominator == 0) throw IllegalArgumentException("denominator can't be zero")
      if (numerator == 0) return ZERO
      if (numerator == denominator) return ONE

      val d = gcd(numerator, denominator)
      return Ratio(numerator / d, denominator / d)
    } // <-- end of static function invoke

    fun gcd(a : Int, b : Int) : Int { /*omitted*/ } // static function!
  }

}

The beauty of Kotlin here is that you'll still be able to use 'constructor' semantics whenever you need to create an instance of Ratio, i.e. you can write as follows as if you had ordinary constructor:

val r1 = Ratio(3, 7) // invoke will be called here
val r2 = Ratio(numerator = 1, denominator = 4) // invoke will be called here

This is it, I hope you find it useful.

Try-with resources in Kotlin

If you’re familiar with Java but not that familiar with Kotlin you might have some difficulties with using standard control structures such as try-with resources.

In Kotlin it is also doable but it looks trickier, for example this code

OutputStreamWriter(myOutputStream).use {
    // you can reference `it` value which is a resource you're working on in this lambda:
    it.write('a')
}

Is an equivalent of the following:

try (final OutputStreamWriter it = new OutputStreamWriter(myOutputStream)) {
    it.write('a');
}

It is interesting that even though Kotlin uses lambda construction it doesn’t introduce any overhead – this code is inlined by Kotlin compiler.

Non-AOP based approach for implementing service layer in Kotlin

Usually when you’re adding a service layer in java you have to deal with so-called cross-cutting concerns in your service such as adding uniform logging for your service methods and dealing with exceptions in a uniform way.

This can be done in multiple ways. As any seasonal programmer knows, direct, brute-force approach when you’re writing this code every time for each method just doesn’t work well. If you’re doing it yourself for every method and every service in your application you’re cluttering your code and just making it harder to maintain and extend as for any significant changes in your cross-cutting code you have to rewrite service implementations for every method. Or, simply put, it requires O(n) efforts, where n is a count of methods in your service layer.

More traditional approach to address this problem is to use AOP. Comparing to the previous approach it requires just O(1) efforts. However AOP is viewed as a hack by OOP purists.

In this article I’ll try to describe another approach which doesn’t use anything besides plain old OOP and at the same time will require the same O(1) efforts for implementing cross-cutting concerns in your code.

So, let’s start. We’ll need a some sort of API for that framework that would support cross-cutting concerns. Let’s define it.

Public API for our framework

If we want to deal with input and output data in a uniform way, we can start with the generic Request and Result traits that would define input and output data for our service calls:

trait Result
trait Request

These traits will merely designate (or mark) your domain objects that will carry the data that will be needed to make a particular request or will identify your service response.

As a service might return result of a remote call or any lengthy operation we can come up with a trait that will represent a result of asynchronous call:

trait Response {
  fun get(): TResult
}

As you may probably noticed this trait looks like a simplified version of Future interface. The reason why we might not want to use Future here is because usually we just don’t need to deal with its complicated interface: it also has get() method and its more complicated counterpart, but using these methods may not be convenient for application programmer as get() waits for a response indefinitely and we may not want to do that as usually we want to work with service that define certain SLA. And the other get(time, timeUnit) method is just not convenient to call as you need to know and set that time manually. In our Response trait we defined get() method out of an assumption that it will deal with setting service timeouts according to those services SLAs (as well as dealing with retries) and we won’t let the consumers of our service API deal with this operational burden.

Also, for our service implementers we’ll need to provide a convenient way of creating Response objects:

trait ResponseCreator {
  fun create(request : TRequest, calcFun: (TRequest) -> TResult) : Response
}

This trait will provide an interface to the service implementer that will abstract away dealing with cross-cutting stuff such as logging request and responses, measuring execution time etc.

Sample Usage

So, here is how it may look like from the service implementer’s point of view:

//
// Sample Service API
//

trait GetUserProfilesRequest : Request {
  val userIds : List
}

trait UserProfile {
  val id : Long?
  val name : String
  val avatarUrl : String
}

trait GetUserProfileResult : Result {
  val profiles : List
}

trait UserService {
  fun getUserProfiles(request : GetUserProfilesRequest) : Response
}

//
// Sample Service Impl
//

data class UserProfileImpl(override val id : Long?,
                           override val name : String,
                           override val avatarUrl : String) : UserProfile

data class GetUserProfileResultImpl(override val profiles : List) : GetUserProfileResult

data class GetUserProfileRequestImpl(override val userIds : List) : GetUserProfilesRequest

class UserServiceImpl(val responseCreator : ResponseCreator) : UserService {
  override fun getUserProfiles(request: GetUserProfilesRequest): Response {
    return responseCreator.create(request, { (r) -> /* hey mom, look at this amazing type inference! */
      val result = ArrayList(r.userIds.size)
      for (id in r.userIds) {
        result.add(UserProfileImpl(id = id, name = "user#${id}", avatarUrl = "http://avatarUrl_${id}"))
      }
      GetUserProfileResultImpl(profiles = result)
    })
  }
}

Sample Framework Implementation

Sample, synchronous implementation of ResponseCreator is shown below (note, that this code also contains “aspect” behavior which may be common for all the services you’re working with in your application):

/**
 * Sample response, in real applications may use Futures with the default service SLA timeouts/retries
 */
class ImmediateResponse(val result : TResult) : Response {
  override fun get(): TResult = result
}

/**
 * Sample response creator
 */
class ImmediateResponseCreator : ResponseCreator {
  override fun create(request: TRequest,
                                         calcFun: (TRequest) -> TResult): Response {
    // [1] log request
    println(" > request = ${request}")
    // [2] start measuring time
    val startTime = System.currentTimeMillis()
    try {
      val result = calcFun(request)
      // [3] stop measuring time
      val timeDelta = System.currentTimeMillis() - startTime
      // [4] log result and execution time
      println(" > result = ${result}, timeDelta = ${timeDelta}")
      return ImmediateResponse(result)
    } catch (e : Exception) {
      // [3] stop measuring time
      val timeDelta = System.currentTimeMillis() - startTime
      // [4] log exception and execution time
      println(" > error = ${e.getMessage()}, timeDelta = ${timeDelta}")
      throw e // rethrow an exception
    }
  }
}

Full listing may be found in this gist.

Links:

Плюс месяц

Надо бы подвести итоги уже более чем месячного пребывания в Америке.

Напряженный ритм первых дней спал, самые главные дела переделаны. Теперь бывают моменты, когда мне кажется, что я все еще в Питере – из-за раннего заката и случающихся дождей, но мелкие детали слишком другой жизни делают эти deja vu очень короткими.

Получил здесь местные водительские права, сдал практику с первого раза. Учитывая то, что на права я сдавал пять лет назад и с тех пор у меня не было машины это было целое достижение. Порадовал инструктор, которому я сдавал на права. Очень спокойный и добродушный американский дядька. Если я ошибался – спокойно мне указывал на ошибку. Набрал 86 баллов, до порога незачета меня отделяло совсем чуть-чуть.

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

После того, как закончился government shutdown сразу пошел получать SSN. Для тех, кто собирается приехать – получайте его в Bellevue, там не такие строгие правила как в Сиэтле, где обыскивают как в аэропорту. Все документы пришли по почте в течении нескольких дней, что вызвало очередной разрыв шаблона. Все-таки непривычно, что вот так, сразу, без взяток вполне нормально получается решать всякие вопросы с чиновниками, которые, что совсем невероятно, вполне себе дружелюбны и готовы помочь.

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

Учитывая, что я переезжал прямо-таки в тепличных условиях, возможно, стоит посмотреть мнения и других людей. Для интересующихся советую govorimpro.us где собрано много разных, в том числе очень непростых, жизненных историй.

Что касается работы – тут все довольно строго с раскрытием даже вполне невинных деталей работы, но в целом впечатление позитивное. Главное, что есть множество работающих продуктов, а среди руководства, насколько я вижу со стороны, нет фантазеров и дураков, одни жесткие прагматики, что, вероятно, послужило причиной такого успеха и уважения на рынке.
Для интересующихся – про Амазон есть неплохая книга.
Один из наших соотечественников опубликовал статью на хабре, правда, надо сказать, некоторые вещи там перевраны.

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

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



Scheduling notifications on Ubuntu in a geeky way

Several days ago I tried to find out a way to schedule notifications on my Ubuntu, mostly for myself. The reason why I bothered about it is because I wanted to get rid of all the distractions namely email notifications, messenger, etc. so that I can verify updates – e.g. new emails without breaking concentration and actually staying in the Flow.

Luckily I’m using Ubuntu as an OS for my work. And this wonderful OS has everything what we need to set up notifications without the need to install extra software, if you geeky enough :)

In order to show notification on Ubuntu you may use wide variety of the existing command line tools, e.g. zenity, notify-send, etc. I picked notify-send as it generates nice auto-dismissable popup notifications, e.g.

notify-send "Check Email: `date`"

For scheduling I used cron.

But the straightforward way of using X-windows-bound things like notify-send won’t work being used by cron directly. This is because cron tasks are executed from the special, cron user. But if this user have sufficient priveleges to read your .Xauthority it is not a problem (and usually it does, and it does on Ubuntu if not specially reconfigured).

So I ended up with the special notification launcher, like this one:

# !/bin/bash

export DISPLAY=:0.0;

# $HOME/.Xauthority
export XAUTHORITY=/home/alex/.Xauthority

/usr/bin/notify-send "Check Email `date`"

I saved this script under the name /home/alex/notify_email

Then I added cron task that was able to launch my script. You may manage your cron tasks by using crontab utility, e.g. list existing task – sudo crontab -l and edit cron task by invoking sudo crontab -e.

My configuration looked as follows:

# m h  dom mon dow   command

40 * * * 1,2,3,4,5  /home/alex/notify_email

This means – invoke notify_email script once per hour, in each *:40 minutes, each working day (remember – the week starts with Sunday that comes under the number 0).

That’s it. Have fun!

О работе, сдаче на права и поезке к океану

Закончилась первая неделя на новой работе, из-за стресса, информационного перегруза и большого количества дел и впечатлений не мог делать заметки в блоге. Чтобы вы могли оценить “маштаб трагедии” замечу, что за первую неделю получил больше 500 писем по email, не говоря уже о большом количестве документации, которую надо хорошо осознать. To make matters worse, как говорится, еще нужно общаться на английском.

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

Пока расскажу совсем кратко. Очень много людей разных национальностей и разного цвета кожи. По первым впечатлениям от процесса – первые дни напомнили мою работу в офисе Моторолы лет шесть тому назад – есть сходные черты, вызвавшие deja vu, хотя подходы этих двух компаний очень сильно отличаются. Рядом с офисом есть парк у озера – Lake Union, как на моей бывшей работе в Yota в Петербурге.

Что касается технических вещей – объем нагрузок, с которым приходится иметь дело инженерам Amazon впечатляет. Из-за специфических и довольно сложных задач компании приходилось находить свои уникальные подходы. В открытых источниках можно найти информацию о том, что, например, в Amazon есть мощный инструмент деплоя apollo, о котором рассказывает Joy Jenkins в докладе “Velocity Culture”.

Сегодня сделал еще два нужных дела – сдал на 100% местную теорию по вождению. Сдал теорию минут за пять вместе с заполнением бумажек и оплатой комиссии за экзамен, равной 15 долларам. Был приятно удивлен отсутствием всяческой бюрократии. Тесты, кстати, доступны бесплатно для всех желающих.
Теперь впереди сдача практики по вождению. Поскольку у меня небольшой опыт вождения, я решил взять занятие по вождению, совмещенное со сдачей экзамена. Сначала инструктор проведет занятие, на котором мы проделаем все элементы, требуемые на экзамене, а после занятия ему же надо будет показать их уже на зачет. Сдавать можно на своей машине, но можно машину взять на месте – это стоит 10 долларов.

Второе нужное дело – подписали контракт на аренду жилья. Очень непривычная вещь для приезжающих из России. Мы подписывали контракт на стандартных условиях. Контракт заключается обычно на длительный срок – мы заключали его на год, некоторые коллеги заключали на полтора года.
В контракте оговорена фиксированная арендная плата, правила проживания и т.п. Поскольку ни у меня, ни у Веры не было кредитной истории – с нас попросили дополнительно внести security deposit – дополнительный взнос, равный месячной арендной плате. Этот security deposit возвращается после года проживания, при условии, что жильцы ничего не поломали и вносили в срок арендную плату. Мелкие поломки оплачиваются отдельно. За составление договора нужно было внести еще дополнительную плату – так называемый application fee в нашем случае равный 42 доллара на каждого взрослого, на которого оформляется договор. Дом сдает компания, занимающаяся сдачей помещений в аренду. Квартира сдается без мебели, но с кухней, стиральной машиной, плитой, микроволновкой, стиралкой, посудомойкой и сушилкой. О сушилке я раньше не знал – насколько удобный и полезный агрегат, но почему-то в России не очень распространенный.

Квартиру мы сняли в том же доме, в котором живем сейчас, так что переезд обещает быть относительно простым и безболезненным. В конечном счете в выборе места сыграли роль несколько факторов: возможность в течении 15 минут неторопливо дойти пешком до работы, относительная близость магазинов и парков, небольшая удаленность от центра города и отсутствие (пока) машины. Да и цена была человеческой, правда квартиру мы сняли, что называется в последний момент – оставалось всего две свободных квартиры из предложений которые нас устраивали по цене.

Менеджер, который оформлял договор был удивительно похож на Шелдона из “Теории большого взрыва”, даже голос похож. Я все ждал какой-нибудь заумной фразы из квантовой механики.

В прошедшие выходные была очень хорошая ясная погода, по прогнозу было +21 C, по ощущениям все 25. В субботу мой новый коллега Денис со своей женой пригласил нас прокатиться к океанскому берегу – по пути проезжали небольшой, симпатичный городок Абердин – если кто не знает родина Нирваны.



Aberdeen

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

В заключение – насколько фотографий с побережья Тихого океана

О дожде, стрессе от переезда и политкорректности

Сначала о дожде. В Петербурге за 12 лет я никогда такого не видел – непрерывный, часто довольно сильный дождь в течении четырех дней. Вчера плюнули на дождь и пошли гулять с ребенком в местный небольшой парк на Lake Union. Забавно, но даже в дождь довольно много людей поддерживает здоровый образ жизни – бегают и катаются на велосипедах. Сегодня дождь, наконец, закончился, и в городе в нескольких местах появилась радуга, было очень красиво в момент отступления грозовых туч и появления солнца.

Несколько дней после перелета были непростыми в смысле привыкания к новому времени и, возможно, дождь оказался весьма кстати. Сам я чуть не заболел после перелета, но за пару дней вместо прогулок отоспался и пришел в норму. Вообще, из такой жесткой встряски можно извлечь что-то полезное – здесь без проблем удалось наладить режим дня, чтобы вставать не около полудня как мы привыкли в Питере, а в 6:30 – 7 утра. Большая куча дел заставляет волей-неволей учиться регулярно планировать свои дела и серьезно концентрироваться на них, проводя за компом столько времени, сколько нужно для дела и тратя его по минимуму на всякую чушь вроде чтения новостей. Было бы здорово закрепить впредь такое поведение.

В процессе продумывания дел пришла умная мысль, но, к сожалению, слишком поздняя чтобы ей воспользоваться – поскольку работодатель так и так искал временное жилье, надо было бы попросить сразу его там, где скорее всего захочется продлевать аренду, а именно в одном из пригородов Сиэтла, например в Redmond или Bellevue, за адекватную цену. И тогда переезжать не пришлось бы, достаточно было бы перезаключить договор аренды не на месяц, а, к примеру, на год если квартира понравилась. В общем – дарю идею тем, кому она может пригодиться.

Пока писал свою первую заметку думал – стоит ли излагая что-то на русском пытаться быть политкорректным и пытаться найти замену слову “негр”, считающееся неприемлемым у американцев. В конечном счете пришел к выводу, что нет, не стоит заходить настолько далеко – исторически сложилось так, что слово “негр” в русском языке не носит ровно никакой негативной подоплеки, напротив, в бывшем СССР употребляли это слово как синоним угнетенных, которым стремились помочь. Пример такого виден и в кино – “Максимка”, 1952 г –

1864 год. Русский корвет «Богатырь» в водах Атлантического океана встречает американское судно. В его трюме живой товар — негры. Через два дня матросы «Богатыря» снимают с обломков разбитого штормом американского корабля мальчика-негра. Запуганный и забитый работорговцами, негритёнок находит в русских матросах добрых и отзывчивых людей. Вскоре мальчик, прозванный матросами Максимкой, становится любимцем всей команды.

© Wikipedia

Да что там говорить, всеми любимый в России Пушкин сам по крови прадеда отчасти был негром.

28 сентября

Последние пару дней практически непрестанно шел дождь, так что наши прогулки по городу пришлось отложить. Впрочем, в редкие моменты прояснения погоды удалось открыть счет в местном банке, взять местный пакет мобильной связи и прицениться к местной недвижимости с точки зрения съема.

Впрочем по порядку. Я открывал счет в First Tech Credit Union – это довольно интересный экземпляр из т.н. “Credit Union” серии банков в штатах. Необычность этого банка в том, что открыть счет там могут только сотрудники высокотехнологичных компаний, типа Boeing, Google и Amazon. Аналогично есть Teachers Credit Union – соответственно для американских учителей. Открытие счета “для своих” имеет ряд выгодных особенностей, в числе которых очень толерантное отношение к временному отсутствию SSN – можно пользоваться счетом и расплачиваться чеками и по карте сразу после похода в банк, после получения SSN можно получить кредит на относительно выгодных условиях. К примеру, на машину ставка по кредиту составит примерно 4% годовых – это очень хорошее предложение по сравнению с другими банками, которые без кредитной истории, по рассказам, могут выставить ставку чуть ли не до 20 процентов за автокредит.

Мобильная связь здесь в целом дороже и хуже по качеству, чем в России. Самый дешевый вариант, который только можно было взять, без интернета – prepaid, AT&T – обошелся мне в 50 долларов – 25 долларов за симку (что-то вроде активации) + 25 долларов как минимальный платеж на счет. В некоторых местах, недалеко от центра города, телефон просто не ловит. Удивительно хорошая по качеству связь по скайпу, учитывая географическую отдаленность.

В процессе обзора здешнего съемного жилья оказалось, что в самом городе пары с детьми предпочитают не жить, выбирая более просторные, спокойные и зеленые пригороды – Redmond, Bellevue, Kirkland. По пути Яна (агент по переездам) рассказала, что некоторые новые квартиры сдаются со значительной скидкой за первый месяц, а, зачастую плату не берут совсем – так пытаются привлечь клиентов. В сумме с довольно высоким спросом на жилье это было довольно неожиданно слышать.

Цены на аренду высоки по Питерским меркам – неплохая “однокомнатная” – 1 bath 1 bedroom квартира оказалась по цене примерно 1500$/месяц. Правда это была просторная трехэтажная квартира в коттедже, где на первом этаже был просторный гараж для машины, на втором – кухня с гостиной и спальня на третьем. Можно подобрать более скромные варианты, приближающиеся к 1000$/месяц. Все квартиры сдаются, как правило, без мебели, но с кухней, плюс, обычно, стиралкой и сушилкой. Меблированные квартиры как правило очень крутые и цена вопроса обычно составляет не менее 2000$/месяц.

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

По мере ознакомления с местной процедурой по сдаче на права обнаружилась удивившая меня вещь: оказывается в Америке не только доступна местная методичка по американским ПДД на русском, но и сам экзамен по теории можно сдавать на русском.
Любопытствующие могут убедиться лично, вот вариант на русском, вот на английском.

До этого слышал, что и New York Times теперь выходит в том числе и на русском, а так же информационные надписи в метро Нью-Йорка дублируют на русском. И при этом никто не кричит о засилии русских. Удивительная толерантность.

25 сентября, первый день в Сиэтле

Свершилось, наконец перелет завершен.


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


Оказалось, что местные знают о России и, как правило, несколько слов на русском. Таможенник, проверяющий паспорт все повторял на русском: “До свидания, я студент!”.

Очень много людей разных национальностей – японцы, корейцы, негры, индусы, китайцы. Таксист, подвозивший нас был натуральным индусом – в огромной белой чалме и в синем халате (или тоге, не знаю как правильно). Нас удивило, что индус сразу узнал в нас русских и сразу угадал, что я еду работать в Амазон.


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


Люди тут совершенно иные, чем в Питере, первое, что бросается в глаза – это необычайная (по моим меркам неопытного путешественника) доброжелательность. На улице подошел негр, когда я тут немного заплутал (он меня видел в магазине) и показал, как обойти ремонтирующуюся улицу впереди и даже проводил до нужного места, рассказав, что у него несколько друзей в России.

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


Такое ощущение, что город на капитальном ремонте – по ощущениям чуть не каждая вторая улица ремонтируется, полно строек и каких-то ремонтов вокруг, при пешей прогулке часто попадаются знаки Sidewalk Closed вместе с ограждением, что означает, что дальше по тротуару идти нельзя из-за ремонта и надо переходить на другую улицу. При этом, как правило, для машин дорогу не закрывают – если и есть дорожные работы, то улицу стараются оставить хоть минимально проходимой для машин, но не для пешеходов. Удивляло немного как люди на пробежках тут выбирают маршрут. Я бы при таких исходных не сунулся бы бегать на улицу.


Приютили нас в Dexter Apartments, на Dexter Avenue, что рядом с Lake Union. Несмотря на усталость от почти суточного перелета (если считать с момента выхода из дома в СПб) были под впечатлением от квартиры – одна ниша для одежды площадью как кухня на нашей старой съемной квартире в корабле.

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

В местном магазине (QFC, Mercer St 500) почувствовал себя полной деревней – хоть он и напоминал нашу пятерочку, но обслуживание было иным, продукты были совершенно другие – например чувствуется тяга американцев к большим вещам и как правило сходные вещи – пакет молока, булка и т.п. – здесь больше по размеру. Не смог найти гречку, которую так просила жена, обошел магазин раз пять :)


В свое время я как-то пропустил мимо ушей замечание о том, что в Америке другие розетки – и сразу по приезду ни одно устройство не удалось подключить к сети. Тут и пригодился подаренный коллегами c прошлой работы Power Bank. Впрочем в другом магазине – City Hardware внешним стилем будто переехавшим с дикого запада 19-го века оказались переходники для евровилок на американские розетки.


Ну вот, кажется на сегодня хватит.