суббота, 2 ноября 2013 г.

Ещё про QueryInterface

Про меня - все всё поняли.

http://18delphi.blogspot.ru/2013/11/supports.html?showComment=1383353862701#c88813335274335154
"Странные вещи пишите Александр... Очень странные...
<-- Документация System.SysUtils.Supports
Indicates whether a given object or interface supports a specified interface. 

Call Supports to determine whether the object or interface specified by Instance, or the class specified by AClass, supports the interface identified by the IID parameter. If Instance supports the interface, Supports returns the interface as the Intf parameter and returns True. If AClass supports the interface, Supports does not return an interface, but still returns True. If the interface specified by IID is not supported, Supports returns False. 
--> 
Обратите внимание на упоминание в документации слов objects, class, instance.
Из документации явно следует, что SysUtils.Supports предназначена (задумывалась авторами) для проверки, поддерживает ли данный класс или объект (экземпляр класса) данный интерфейс. Иными словами: умеет ли *данный объект* «делать это»? Обратите внимание — объект, а не кто-то, кто как-то связан с этим объектом или кого можно получить, зная объект."

Ну меня дурака - положим "умный дядя научил".

Но вот незадача.

Других-то - не научил:

function TCustomForm.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  // Route the QueryInterface throught the DesignerHook first
  if (DesignerHook = nil) or (DesignerHook.QueryInterface(IID, Obj) <> 0) then
    Result := inherited QueryInterface(IID, Obj)
  else
    Result := 0;
end;
...
function TCorbaImplementation.QueryInterface(const IID: TGUID;
  out Obj): HResult;
begin
  if Assigned(FController) then
    Result := IObject(FController).QueryInterface(IID, Obj) else
    Result := ObjQueryInterface(IID, Obj);
end;
...
function TComponent.QueryInterface(const IID: TGUID; out Obj): HResult;
begin

  if FVCLComObject = nil then
  begin

    if GetInterface(IID, Obj) then Result := S_OK
    else Result := E_NOINTERFACE

  end
  else
    Result := IVCLComObject(FVCLComObject).QueryInterface(IID, Obj);

end;
....
function TCustomWebAppDataModule.QueryInterface(const IID: TGUID;
  out Obj): HResult;
begin
  if IsEqualGuid(IID, IGetWebAppComponents) then
    if Supports(AppServices, IGetWebAppComponents, Obj) then
    begin
      Result := S_OK;
      exit;
    end;
  Result := inherited QueryInterface(IID, Obj);
end;
...
function TCustomWebAppPageModule.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if IsEqualGuid(IID, IGetWebAppComponents) then
    if Supports(AppServices, IGetWebAppComponents, Obj) then
    begin
      Result := S_OK;
      exit;
    end;
  Result := inherited QueryInterface(IID, Obj);
end;
...
function TXPInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin

  if (FDelegator = nil) or FIntrospective then
    Result := inherited QueryInterface(IID, Obj)
  else
    Result := IInterface(FDelegator).QueryInterface(IID, Obj);

end;
....
function TNestedScope.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  Result := E_NOINTERFACE;
  if GetInterface(IID, Obj) then
    Result := S_OK
  else
    if Supports(Inner, IID, Obj) or Supports(Outer, IID, Obj) then
      Result := S_OK;
end;
....
function TVirtualObjectMemberInstance.QueryInterface(const IID: TGUID;
  out Obj): HResult;
begin
  // depending on the custom wrapper type, it returns the appropriate interfaces
  // that the virtual wrapper is supposed to support
  if ((IID = IValue) or (IID = IInvokable) or (IID = IArguments)) and
     (not Supports(GetCustomWrapper, IID)) then
    Result := E_NOINTERFACE
  else
    Result := inherited QueryInterface(IID, Obj);
end;
....
procedure TStyledWindowBorder.MouseMove(Shift: TShiftState; X, Y: Single);
var
  P: TPointF;
  Obj: IControl;
  SG: ISizeGrip;
  NewCursor: TCursor;
  CursorService: IFMXCursorService;
begin
  NewCursor := crDefault;
  TPlatformServices.Current.SupportsPlatformService(IFMXCursorService, IInterface(CursorService));
  FMousePos := PointF(X, Y);
  if Assigned(FCaptured) then
  begin
    if Assigned(CursorService) then
    begin
      if ((FCaptured.QueryInterface(ISizeGrip, SG) = 0) and Assigned(SG)) then
        CursorService.SetCursor(crSizeNWSE)
      else
        CursorService.SetCursor(FCaptured.Cursor);
    end;
    P := FCaptured.ScreenToLocal(PointF(FMousePos.X, FMousePos.Y));
    FCaptured.MouseMove(Shift, P.X, P.Y);
    Exit;
  end;

  Obj := ObjectAtPoint(FMousePos);
  if Assigned(Obj) then
  begin
    SetHovered(Obj);
    P := Obj.ScreenToLocal(PointF(FMousePos.X, FMousePos.Y));
    Obj.MouseMove(Shift, P.X, P.Y);
    if ((Obj.QueryInterface(ISizeGrip, SG) = 0) and Assigned(SG)) then
      NewCursor := crSizeNWSE
    else
      NewCursor := Obj.Cursor;
  end
  else
    SetHovered(nil);
  // set cursor
  if Assigned(CursorService) then
    CursorService.SetCursor(NewCursor);
  FDownPos := FMousePos;
end;
....
function TCommonCustomForm.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  // Route the QueryInterface through the Designer first
  if not Assigned(Designer) or (Designer.QueryInterface(IID, Obj) <> 0) then
    Result := inherited QueryInterface(IID, Obj)
  else
    Result := 0;
end;
....
function TServerEventDispatch.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
  begin
    Result := S_OK;
    Exit;
  end;
  if IsEqualIID(IID, FServer.FServerData^.EventIID) then
  begin
    GetInterface(IDispatch, Obj);
    Result := S_OK;
    Exit;
  end;
  Result := E_NOINTERFACE;
end;
....

У меня наверное с русским языком что-то не то...

http://18delphi.blogspot.ru/2013/11/blog-post_4367.html?showComment=1383369874239#c5819572482489216617

"Преждевременные оптимизации здесь - зло."

ПРЕЖДЕВРЕМЕННЫЕ оптимизации - ВООБЩЕ ЗЛО.

Зло - АБСОЛЮТНОЕ.

Но разве я об этом спрашивал?

Ещё о ФЯ...

Человек, который пишет на ФУНКЦИОНАЛЬНОМ языке - должен ПОНИМАТЬ - обеспечивает ли "его язык" оптимизацию "хвостовой рекурсии"? Или не должен?

ДОЛЖЕН ли он понимать - обеспечивает ли "его язык" кеширование результатов вычисления функций? Или не должен?

Должен ли он вообще задумываться о "таких деталях реализации" "его языка"? Или не должен?

Напишет ли он эффективный код, если не задумывается об этом?

Вопросы - ОТКРЫТЫЕ.

Вдогонку.. О ФЯ, теории и практике применения...

Вдогонку вот к этому - http://18delphi.blogspot.ru/2013/11/blog-post_2.html

Вот скажем "чистый функциональный язык" без "состояний и императивных конструкций (типа циклов)" сможет вычислить Factorial(100000)? И без таких "частностей" как "оптимизация хвостовой рекурсии".

Ему "теоретически" это под силу? На реальном, обыденном, современном нам компьютере.

Ещё раз про Supports

Навеяно вот этим - http://18delphi.blogspot.ru/2013/10/supports.html?showComment=1383223827244#c6258577626124847121

Почему-то МНОГИЕ (если не все) видят в моих постах какое-то "искание абсолюта" или "попытки быть гуру" и построить "коня в вакууме.

Вот ОТНЮДЬ не так.

Я не ищу абсолюта, и не пытаюсь добыть серебряную пулю или построить "сферичаского коня в вакууме".

Напротив!

Я - ПРАКТИК.

РЕМЕСЛЕННИК,

Без ВЫСШЕГО ОБРАЗОВАНИЯ.

Я не пытаюсь найти "универсальный рецепт".

Наоборот. Я ЛИЧНО считаю, что УНИВЕРСАЛЬНЫХ рецептов - НЕТ.

Я пишу лишь о том, с чем столкнулся НА ПРАКТИКЕ.

И я - НИКОГО НИКУДА не "тяну". И НИКОМУ НИЧЕГО - не навязываю.

Про Supports - я написал БАНАЛЬНУЮ вещь - "можно сделать так", а "можно сделать так".

И понятно, что решения - не РАВНОЗНАЧНЫ.

Одно - БОЛЕЕ ОБЩЕЕ, другое - БОЛЕЕ ЭФФЕКТИВНОЕ.

Ну по моей ПРАКТИКЕ.

Не более того.

Я отношусь с ГЛУБОКИМ ПРЕДУБЕЖДЕНИЕМ к Supports и QueryInterface.

Это - ПРАВДА.

И стараюсь их не применять "при прочих равных". Например при помощи такой "техники", как была описана по ссылке выше. А вообще говоря - техник много. Для РАЗНЫХ "частных случаев".

И это - продиктовано - моей ПРАКТИКОЙ. Не более того.

Просто был момент - когда появились интерфейсы, то все увидели - "о! интерфесы - круто".

И "УВЛЕКЛИСЬ" использованием интерфейсов вообще и Supports - в частности.

И я не был "счастливым исключением".

И горько поплатился за это. Временем жизни проведённым в отладке и под профайлером.

А "время жизни" - это - самое ЦЕННОЕ. (Люди "за 40-к" - меня наверное поймут)

Попробую привести пример.

В эпоху ПОВАЛЬНОГО УВЛЕЧЕНИЯ интерфейсами (когда все мы были молоди и казалось, что "море по колено") - Я САМ ЛИЧНО "родил" следующую ИДИОТСКУЮ конструкцию:

TmyObject = class(TPersistent)
 f_Owner : TPersistent;

 function GetOwner: TPersistent; override;
 begin
  Result := f_Owner;
 end;

 procedure SetOwner(anOwner : TPersistent);
 begin
  f_Owner := anOwner;
 end;
 
 function QueryInterface(anIntf : TGUID; out Obj): hResult;
 begin
  if Self.GetInterface(anItf, Obj) then
   Result := S_OK
  else
  if Supports(f_Owner, anIntf, Obj) then
   Result := S_OK
  else
   Result := E_NoInterface;
 end;
end;//TmyObject 

Что тут написано?

У объекта - БЫВАЕТ владелец.
Если у объекта запрашивают интерфейс, то мы сначала пытаемся получить интерфейс у объекта.
Если это НЕ ПОЛУЧИЛОСЬ, то пытаемся получить интерфейс у ВЛАДЕЛЬЦА.

И так - "по рекурсии".

Код конечно - ИДИОТСКИЙ. Я СЕЙЧАС - это - ПОНИМАЮ. Но не 15-ть лет назад скажем.

Если вы сразу скажете, почему он идиотский - я пожму вам руку. Мой вам "респект и уважуха". Можете дальше не читать "старпёра"...

Но "когда-то" мне казалось, что код - "очень даже правильный".

И что "делает, что должен".

Если не получили интерфейс у объекта, то пытаемся получить интерфейс у его владельца. "логично"...

И во-многих случаях - "РАБОТАЕТ".

Опустим те случаи, где при таком подходе отдавались "паразитные интерфейсы". Их было НЕМНОГО и для них имелись ОТДЕЛЬНЫЕ "костыли" и "заточки"...

А в ЦЕЛОМ - ВСЁ работало КАК НАДО.

КАК И ЗАДУМЫВАЛОСЬ...

Только ПОТОМ, я многие ЧАСЫ провёл под отладчиком и профайлером в поиске ответа на вопрос "что же ТАК всё тормозит".

А ВСЁ ОЧЕНЬ ПРОСТО - во-первых GetOwner - НЕ САМЫЙ быстрый метод (он - dynamic, а не virtual, если я не ошибаюсь), да и бог бы с ним...

ВЛОЖЕННОСТЬ объектов БЫЛА (да и есть) ДОСТАТОЧНО - глубокая.

А тут начинал играть роль другой фактор.

Спросили интерфейс у объекта. Он его не поддерживает. Тогда спросили у родителя. Он - тоже не поддерживает. Тогда у следующего родителя. Он - ТОЖЕ НЕ ПОДДЕРЖИВАЕТ. И так ПО ВСЕЙ ЦЕПОЧКЕ. И в итоге - ПРОБЕЖАЛИСЬ ПО ВСЕЙ ЦЕПОЧКЕ. Сделали МАССУ вычислений. А получили nil и E-NoInterface. Что и следовало ожидать.

А таких Supports и QueryInterface - МАССА. Всяких разных. Для РАЗНЫХ ЦЕЛЕЙ.

На самом деле.

И КАЖДЫЙ делает ВЫЧИСЛЕНИЯ, которые "на поверку" - НЕ НУЖНЫ.

В конечном итоге профайлер и отладчик показали мне всё это и я ИЗБАВИЛСЯ от ЭТОГО УЖАСА.

Но это заняло "много ценного времени моей жизни". Которое я мог бы потратить на что-тто другое.

Может быть - я - БОЛВАН и ИДИОТ, что сотворил такой АД и УЖАС. Может быть - вы УМНЕЕ меня и вам повезло.

Но только "подобный" АД и УЖАС - я наблюдал у разных других разработчиков. Под "разными соусами".

Так что - я тут "явно не одинок".

Если вы умнее "нашей банды" ИДИОТОВ и БОЛВАНОВ - вам - повезло :-)

Но к Supports и QueryInterface я ЛИЧНО - "отношусь с предубеждением".

Если хотите ЕЩЁ примеров - пишите. Приведу - ЕЩЁ.

И не только из СВОЕГО ЛИЧНОГО опыта.

А так...

Я никого "никуда не тяну", НЕ ПОУЧАЮ и не "стараюсь помогать"...

Просто хочется ПОКАЗАТЬ ОШИБКИ, которые я САМ ЛИЧНО СДЕЛАЛ. Чтобы предостеречь ДРУГИХ людей от этих ошибок.

"Умные учатся на чужих ошибках, а дураки - на своих"...

Хотя на Руси - обычно это не верно....

P.S. Пора переименовывать блог в "записки старпёра"... Оно так вернее суть отражает.. Да и люди может быть будут относиться со "снисхождением".. Чего уж с убогого взять...

О функциональных и императивных языках. О функциональном и императивном подходах

Скажем так "вводная" была тут - http://18delphi.blogspot.com/2013/11/blog-post_4093.html

Сегодня вышел спор с одним из коллег по цеху.

О том, кто и как понимает функциональное и императивное программирование и их принципы. И "что чем является".

Дискуссия местами велась в стиле "Ленин vs Мартов", ну или "Маркс vs Каутский". Ну или "уроки Октября" и "об уроках Октября".

И ОБЕ стороны - "были хороши".

В итоге наговорили друг другу некоторое количество "не очень приятных вещей".

Ну и разошлись - "каждый довольный собой".

Если бы была возможность "расстрелять оппонента", то "расстреляли бы". Ну или на худой конец - "сослали бы в Алма-Ату".

Что хочу сказать?

Ну я ЛИЧНО собой-то не очень "остался доволен".

И видимо заслуженно получил упрёки в том, что "я не слушаю", да и вообще - "никто не просил помогать".

Наверное.

Посему - хочу всё же - "прояснить ситуацию".

И ОБЪЯСНИТЬ - "своё виденье" вопроса. И ЧТО ИМЕННО я пытался донести.

НЕ ПОТОМУ, что "я прав".

В данном вопросе - "правых нет". По-моему.

Есть лишь - "личные трактовки".

Хотя может быть "моя личная трактовка" - ДЕЙСТВИТЕЛЬНО - "ЛИЧНАЯ" и сторонников у меня нет.

ДОПУСКАЮ.

Мне это - НЕ НОВО.

Теперь - к делу.

Суть вопроса была примерно в следующем - "какие языки считать функциональными и почему, а какие не считать".

Мой оппонент приводил следующее определение:
"Функциона́льное программи́рование — раздел дискретной математики и парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних (в отличие от функций как подпрограмм в процедурном программировании).
Противопоставляется парадигме императивного программирования, которая описывает процесс вычислений как последовательное изменение состояний (в значении, подобном таковому в теории автоматов). При необходимости, в функциональном программировании вся совокупность последовательных состояний вычислительного процесса представляется явным образом, например как список.
Функциональное программирование предполагает обходиться вычислением результатов функций от исходных данных и результатов других функций, и не предполагает явного хранения состояния программы. Соответственно, не предполагает оно и изменяемость этого состояния (в отличие от императивного, где одной из базовых концепций является переменная, хранящая своё значение и позволяющая менять его по мере выполнения алгоритма)."
взято отсюда - http://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5

И он НАСТАИВАЛ на своей точке зрения, что мол "раз нет состояний" - значит ФУНКЦИОНАЛЬНОЕ программирование.

Ну и с эти не поспоришь в общем-то. Да я с ЭТИМ И НЕ СПОРИЛ.

Наверное просто был НЕ ТАК ПОНЯТ.

Но!

Я НЕ ЗРЯ написал про "теорию и ПРАКТИКУ ПРИМЕНЕНИЯ".

Собственно какую мысль я пытался донести до своего оппонента?

А вот какую:

"нет состояний" и "нет императивных конструкций" - это конечно - ХОРОШО.

Но! ЗАЧЕМ ЭТО? Суть вопроса в чём?

Зачем отказываться от "состояний" и "императивных конструкций".

Зачем "усложнять себе жизнь". Спрашивал я.

Только ради "чистоты арийской расы"? Только ради "применения чистого функционального программирования"?

Я не спорю, что ЛЮБОЙ язык без состояний и императивных конструкций является ФУНКЦИОНАЛЬНЫМ. (Хотя - как же "монады"? Это разве не "императивная надстройка? Сейчас меня побьют ногами...)

Но! Вот давайте возьмём Паскаль. На нём можно программировать в ФУНКЦИОНАЛЬНОМ СТИЛЕ (согласно ПРИВЕДЁННОМУ ВЫШЕ ОПРЕДЕЛЕНИЮ)?

Можно?

Ну в общем - в большинстве случаев - МОЖНО.
НУЖНО ЛИ? Ну наверное - В БОЛЬШИНСТВЕ случаев - НУЖНО.

Зачем?

Для того чтобы повысить стабильность кода.

Согласен.

Является ли Паскаль после этого "функциональным языком"?

Я - НЕ ЗНАЮ.

Хотелось бы услышать мнение моего оппонента.

Что же говорил я?
 В чём СУТЬ разногласий?

Я говорил примерно следующее - "ТЕОРИЯ - это ХОРОШО. Отсутствие состояний и "императивных конструкций" - ЗДОРОВО. НО ЗАЧЕМ?

Давайте "погрузимся в байты". Говорил я.

Давайте прочитаем СЛЕДУЮЩИЙ АБЗАЦ.

Вот он:
"На практике отличие математической функции от понятия «функции» в императивном программировании заключается в том, что императивные функции могут опираться не только на аргументы, но и на состояние внешних по отношению к функции переменных, а также иметь побочные эффекты и менять состояние внешних переменных. Таким образом, в императивном программировании при вызове одной и той же функции с одинаковыми параметрами, но на разных этапах выполнения алгоритма, можно получить разные данные на выходе из-за влияния на функцию состояния переменных. А в функциональном языке при вызове функции с одними и теми же аргументами мы всегда получим одинаковый результат: выходные данные зависят только от входных. Это позволяет средам выполнения программ на функциональных языках кешировать результаты функций и вызывать их в порядке, не определяемом алгоритмом и распараллеливать их без каких-либо дополнительных действий со стороны программиста (см.ниже Чистые функции)"

Отсутствие СОСТОЯНИЙ, т.е. ДЕТЕРМИНИРОВАННОСТЬ - она КОНЕЧНО ВЕДЁТ к стабильности кода "самого по себе".

Представим язык "без глобальных переменных" и всё - СТАНЕТ ЯСНО.

И ТУТ - мой оппонент - ПРАВ!

БЕЗ глобальных переменных - СТАБИЛЬНОСТЬ повышается.

Но! Спрашивал я.

Не МНОГИМ ли мы жертвуем ОТКАЗЫВАЯСЬ от "состояний" и "императивных конструкций"?

ЗАРАДИ ЧЕГО такие жертвы?

Не РАДИ ли - кешируемости, параллелизма и оптимизации порядка выполнения?

Не ЭТО ли должно быть ПОСТАВЛЕНО ВО ГЛАВУ УГЛА?

Т.е. "отсутствие состояний" и "императивных конструкций" - это ОПРЕДЕЛЕНИЕ. Но! ОПРЕДЕЛЕНИЕ - определят ли ПАРАДИГМУ и СУТЬ функциональных языков.

По-моему - НЕТ.

По-моему - ГЛАВНОЕ как раз, что "мы жертвуем состояниями и императиивностью" ВО ИМЯ "кешируемости, параллелизма и оптимизации порядка выполнения (в частности хвостовой рекурсии").

ВО ИМЯ возможности БОЛЕЕ ЭФФЕКТИВНЫХ вычислений.

А ИНАЧЕ - ЗАЧЕМ ВСЯ эта функциональная парадигма? Чем она ЛУЧШЕ ИМПЕРАТИВНОЙ?

Глобальные переменные - ОТЛОЖИМ В СТОРОНУ. Тут - Я СОГЛАСИЛСЯ. Они - УХУДШАЮТ СТАБИЛЬНОСТЬ.

Если следовать "чистой теории", то "кастрированный" Паскаль - ведь является функциональным языком?

ЯВЛЯЕТСЯ? Я - не знаю. Вопрос к моему оппоненту. Мне кажется, что - ЯВЛЯЕТСЯ.

Могу ошибаться.

Многие ли люди согласятся программировать на подобном языке? Получат ли они выгоду?

Мне кажется, что - нет.

Могу ошибаться.

Так "функциональность языка" это ли лишь "отсутствие состояний и императивных конструкций". Или всё же функциональность "подразумевает" - "кешируемость, параллелизм и оптимизацию вычислений"?

Не знаю.

Для меня - ВОПРОС ОТКРЫТ.

За данный вопрос я получил "ярлык" - "ты придумываешь свои определения".

Может быть.

Только - НЕ ПРИДУМЫВАЮ, а ПЫТАЮСЬ ПОНЯТЬ.

Ну и ещё я получил упрёк - "ты всюду пихаешь свой Форт и форт-машину".

Опять же - НЕ "пихаю". А ПЫТАЮСЬ поделиться.

Я - НЕ НАВЯЗЫВАЮ НИЧЕГО.

Я лишь пишу - О ЧЁМ ЗНАЮ.

Когда мне говорят - "никто не просил помогать" - ну грустно, чего уж...

НЕ просил - простите. Больше не буду.

Я может быть только "неправильно выражаюсь". Форт и форт-машина - всё же РАЗНЫЕ вещи, хотя и связанные.

Точнее даже - "стековая-машина".

Ну практически все компиляторы - ПОСТРОЕНЫ на ней. Ну читаем например статью Болье - "методы построения компиляторов".

И когда я писал про форт-машину - я НИКОИМ ОБРАЗОМ не имел в виду - "разбор токенов по пробелам" или "обратную польскую запись". Эти ЧАСТНОСТИ - УЖЕ сильно выше. В реализации. Я имел в виду лишь стековую-машину и "словарь".

То и другое, так или иначе - присутствует во всех современных компиляторах.

Иногда словарь называется "деревом разбора".

(Или я опять "придумываю собственные определения"?)

Пи-код, байт-код и прочее и прочее - всё из одной и той же области.

И если я ПРЕДЛАГАЛ, для РЕАЛИЗАЦИИ функционального языка использовать форт-машину (стековую-машину) это НЕ ЗНАЧИТ, что я предлагал ОТКАЗАТЬСЯ от ФУНКЦИОНАЛЬНОСТИ. Просто мне КАЗАЛОСЬ, что для КОНКРЕТНОЙ реализации - она очень даже МОЖЕТ ПОДОЙТИ.

http://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D1%82_(%D1%8F%D0%B7%D1%8B%D0%BA_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F)

Испытал влияние:
http://ru.wikipedia.org/wiki/Erlang
"Erlang [ˈɜːlæŋ] — Э́рланг — функциональный язык программирования с динамической типизацией, предназначенный для создания распределённых вычислительных систем. Разработан и поддерживается компанией Ericsson. Язык включает в себя средства порождения параллельных процессов и их коммуникации с помощью асинхронных сообщений и в соответствии с моделью акторов. Программа транслируется в байт-код, исполняемый виртуальной машиной, что обеспечивает переносимость. Кратко формулу языка можно выразить как Erlang = функциональный язык + процессы."

http://ru.wikipedia.org/wiki/Haskell
"
  • YHC (York Haskell Compiler) — форк nhc98, ставящий целью быть более переносимым и эффективным, поддерживает отладчик Hat; генерирует промежуточный байт-код, который можно использовать для генерации кода на других языках программирования"

Всё - взаимосвязано..

Посему резюме:
1. ДОСТАТОЧНЫМ ли условием ЯВЛЯЕТСЯ для "функциональности" языка - ОТСУТСТВИЕ "состояний и императивных конструкций".
2. Можно ли строить "функциональные" языки - на стековой-машине.

-- эти вопросы я оставляю ОТКРЫТЫМИ.

Для своего оппонент скажу лишь. ИМЕННО это я и имел в виду, когда всё писал.
НИ БОЛЬШЕ, НИ меньше.

И никого "поучать" и в мыслях - НЕ БЫЛО.

пятница, 1 ноября 2013 г.

О теории и практике применения

Есть у нас вопросник.

Который мы даём всем кандидатам на роль программиста.

"ПРАВИЛЬНЫХ" ответов - там НЕТ. Ну то есть - ЕДИНСТВЕННО ПРАВИЛЬНЫХ. Мы обычно смотрим, что написал кандидат. Потом - беседуем. Задаём вопросы и смотрим на его реакцию.

Что ПЕРВОЕ мы там спрашиваем?

А вот что. Примерно так - "расскажите про ООП. Что такое инкапсуляция, наследование и полиморфизм".

Зачем эта часть?

А вот зачем - чтобы "проверить", что человек "реально в теме". Что он владеет примерно теми же понятиями, что и мы. И трактует их - примерно так же как и мы.

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

Это - теория.

Есть и вторая часть.

В ней есть например такой вопрос - "когда целесообразнее применять наследование, а когда агрегацию? Чем они отличаются? И что у них общего? Какие у них плюсы и минусы?"

Что мы смотрим тут?

Тут мы смотрим, что человек ПОМИМО знания теории и определений - знает (ну или представляет себе) ПРАКТИКУ ПРИМЕНЕНИЯ.

Это - практика.

Две части ОДНОГО ЦЕЛОГО, называемого ОПЫТОМ.

ТЕОРИЯ и ПРАКТИКА.

И то, и другое может существовать отдельно друг от друга.

И такие случаи я встречал и не раз.

Но. Ценен тот специалист - у которого теория и практика - "встречаются вместе".

Посему я кстати с "лёгким предубеждением" отношусь к людям, который пишут в резюме "знаю 20-ть (придумываю) языков программирования".

Теорию - ДА - знать МОЖНО. А ПРАКТИКА ПРИМЕНЕНИЯ? На практику требуется - ВРЕМЯ. Продолжительное время.

Ну я сужу - лично по себе.

Некоторые мои коллеги возражают мне - "ну есть же люди, которые знают по 40-к естественных языков, которые сложнее". ДА! ЕСТЬ! Тут я развожу руками. Против практики (опять же) не попрёшь. Практика - критерий истины.

Скажем так. МОЁ ЛИЧНОЕ мнение - "ЕСТЬ УНИКУМЫ". Я не из их числа. Посему - я СУЖУ ПО СЕБЕ.

Теперь приведу пример из своей практики.

Я ДОЛГО программирую на Delphi. И у меня - ОБШИРНАЯ ПРАКТИКА ПРИМЕНЕНИЯ.

Некоторое время назад я стал программировать на Objective-C. И могу ли я сказать, что "я знаю Objective-C"?

В "теории" - наверное да. Я знаю синтаксис. Я знаю основные парадигмы. Я знаю даже как так устроен подсчёт ссылок.

Но!

НА ПРАКТИКЕ - я не могу сказать - "я ЗНАЮ Objective-C".

Почему?

Приведу маленький пример.

В API Objective-C есть функция - enumeratorAtPath. Которая возвращает объект-enumerator для перебора дочерних элементов каталога.

Мне надо было перебрать элементы каталога. И я "прочитав по-диагонали" документацию решил, что эта функция мне подходит.

И она - МНЕ ПОДОШЛА. Элементы каталога перебирались, я их сравнивал с какой-то "заданной маской". Отбирал нужные. И ВСЁ ПРЕКРАСНО работало.

Но! Структура каталога со временем - усложнялась и расширялась. Появлялись под-каталоги и под-под-каталоги. ВСЁ ПО-ПРЕЖНЕМУ продолжало работать. Но в какой-то момент - стало "подтормаживать" на мобильных устройствах.

В конечном итоге - я начал разбираться. И "ОКАЗАЛОСЬ", что enumeratorAtPath перебирал не ТОЛЬКО НЕПОСРЕДСТВЕННЫЕ элементы каталога. НО! И ИХ ДЕТЕЙ, причём РЕКУРСИВНО.

Я залез в документацию и прочитал её "УЖЕ не по-диагонали". И там - ВО ВТОРОМ же абзаце БЫЛО чёрным по белому написано. "this function returns enumerator that performs deep enumeration".

Всё чётко и ЯСНО. И надо отдать ДОЛЖНОЕ Apple - они ВСЁ в ДОКУМЕНТАЦИИ написали.

Только кто ж её читает...

Может быть я ОДИН ТАКОЙ БОЛВАН, но мой опыт показывает обратное...

В итоге я нашёл нужную функцию - contentsOfPath. Она правда возвращает НЕ enumerator, а МАССИВ (NSArray). Почему ТАК сделано - это уж на совести Apple. Я бы "конечно" так не сделал бы.

Но! Эта функция МНЕ ПОДОШЛА, заработала как надо и дала "определённый" ЗНАЧИТЕЛЬНЫЙ прирост производительности. В моём "частном случае".

Вот такой пример.

О ТЕОРИИ и ПРАКТИКЕ ПРИМЕНЕНИЯ.

Опять же - может быть я ОДИН ТАКОЙ БОЛВАН и другие бы сразу "сделали всё правильно". Может быть.

НО! Опять повторю - Я СУЖУ ПО СЕБЕ.

И то что я ПИШУ или ГОВОРЮ - проистекает из этих суждений.

Если кому-то мои высказывания кажутся "менторскими" или что "я учу жить" - так вот - КАЖУТСЯ.

Я - СУЖУ ПО СЕБЕ. И ПИШУ и ГОВОРЮ - о СВОЕЙ практике применения. И о том, что ЗНАЮ лично я.

И никому ничего не НАВЯЗЫВАЮ.

Ну кроме может быть - "непосредственных коллег", с которыми я работаю в ОДНОЙ КОМНАТЕ. ДА! Тут я могу (и делаю это) - НАВЯЗЫВАТЬ скажем использование FreeAndNil, а не ПРОСТО Free. Ну или что-то подобное.

Теперь ещё о практике применения.

Если человек программирует на "каком-то" языке - он должен знать только теорию или и ПРАКТИКУ ПРИМЕНЕНИЯ тоже? Он должен знать "внутреннее устройство языка" или нет?

Вот скажем есть функциональный язык. "Какой-то". Человек "должен знать" - КАК он обрабатывает "хвостовую рекурсию" или не должен?

К чему я это? Объясню в следующем посте.

А пока - закругляюсь.

Резюме - теория и практика применения - это НЕОБХОДИМЫЕ составляющие успеха. ВМЕСТЕ. А не порознь.

Хорошая ссылка. Создание форм "с правильным наследованием". Ну грубо говоря

http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1424

Сам так делал в VCM. ДО UML.

гы.. гы..

Ещё кусочек "голого" кода для CoreText

- (void) checkRendered: (IsPageDone) aDone forOperation: (NSOperation *) anOp initCursorIndex: (InitCursorIndex) anInit
{
    if (!myDoc)
        return;
    
    if (anOp || self.stopOnPageBreak) {
        if (myRunnedOp)
            return;
    }
    if (anOp)
        myRunnedOp = YES;
    __block NSAutoreleasePool* vPool = [[NSAutoreleasePool alloc] init];
    @try {
        __block int vPageNum;
        @synchronized (self) {
            if (!myPages)
                myPages = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
            
            vPageNum = CFArrayGetCount(myPages) - 1;
        }
        
        assert(myDoc);
        EVDParaIndex vIndex ([myDoc blockIterator], 0, 0);
        anInit(vIndex);
        __block EVDParaIndex vParaIndex (vIndex);
        
        __block EVDRenderedPage *vPageRenderingNow = nil;
        @try {
            if (vPageNum >= 0) {
                EVDRenderedPage *vPrevPage = (EVDRenderedPage *)CFArrayGetValueAtIndex(myPages, vPageNum);
                if ([vPrevPage RenderedTillEnd]) {
                    //vParaAdded = YES;
                    // - а иначе в ДПЭ в раздел "Воинская служба" в конце добавляется ПУСТАЯ СТРАНИЦА
                    
                    vParaIndex = EVDParaIndex (myDoc, vPrevPage.EndsWithPara);
//                    vParaIndex.rInnerCursors = vPrevPage.EndsWithPara.rInnerCursors;
                    //                vParaIndex = vPrevPage->myEndsWithPara;
                    //                    if (vParaIndex.rOffset == 0)
                    vParaIndex.rPara++;
                    assert(vParaIndex.rPara >= 0);
                    if (vParaIndex.rPara >= INT32_MAX - 100)
                        // - это последняя страница
                        return;
                }
                else {
                    vParaIndex = EVDParaIndex (myDoc, vPrevPage.StartsWithPara);
//                    vParaIndex.rInnerCursors = vPrevPage.StartsWithPara.rInnerCursors;
                    ASSIGN(vPageRenderingNow, vPrevPage);
                    assert(vParaIndex.rPara >= 0);
                    //                    if (vParaIndex.rOffset > 0)
                    //                        vParaIndex.rParaIndex.rPara++;
                }
                {
                    BOOL vDone = NO;
                    aDone(vPageRenderingNow, vParaIndex, vDone);
                    if (vDone)
                        return;
                }
            }
            
            __block BOOL vPageWasFound = NO;
            
            __block BOOL vParaAdded = NO;
            
            __block CGRect vRect;
            initRectForPage(myRenderedForRect, vRect);
            
            __block EVDParaIndex vPrevParaIndex = vParaIndex;
                        
            for (; (vParaIndex.block() != nil); vParaIndex.moveToNextBlock()) {
                id<IevdBlock> vBlock = [[vParaIndex.block() block] retain];
                @try {
                    if (vPageWasFound)
                        if (!vPageRenderingNow)
                            // - типа страница вся была заполнена
                            break;
                    
                    if ((vPageRenderingNow && vParaAdded) || (self.stopOnPageBreak && (vPageNum > 0))) {
                        if (vParaIndex.startsNewPage()) {
                            vParaAdded = NO;
                            [vPageRenderingNow setEndsWithPara: vPrevParaIndex forDoc: self.Doc];
                            DESTROY(vPageRenderingNow);
                            if (self.stopOnPageBreak) {
                                myRunnedOp = YES;
                                break;
                            }
                            initRectForPage(myRenderedForRect, vRect);
                        }
                    }
                    
                    __block EVDRenderContext vCtx (vParaIndex, vRect, vParaAdded);
                    
                    [vBlock render: vCtx : ^(EVDRenderedPara * aRenderedPara){
                        
                            if (anOp && [anOp isCancelled]) {
                                myRunnedOp = NO;
                                vCtx.SetNeedReturn();
                                return;
                            }
                            
                            if ([aRenderedPara frame] || vParaAdded)
                                // - не начинаем НОВУЮ страницу с ПУСТОГО параграфа
                                [self checkRenderedPage: vPageRenderingNow : vPageNum : anOp : vParaIndex : aDone : vPageWasFound];
                            
                        } : ^(){
                            // - тут закончилась страница
//                            if (vPageNum == 428)
//                                NSLog(@"%d", vPageNum);
                            [vPageRenderingNow setEndsWithPara: vParaIndex forDoc: self.Doc];
                            DESTROY(vPageRenderingNow);
                            initRectForPage(myRenderedForRect, vRect);
                            vParaAdded = NO;
                            if (vPageNum % 20 == 0) {
                                [vPool drain];
                                vPool = [[NSAutoreleasePool alloc] init];
                            }
                            if (anOp && [myDoc needCancelPagesCounterWithScale: myScale]) {
                                myRunnedOp = NO;
                                vCtx.SetNeedReturn();
                            }
                            else
                            if (vPageWasFound)
                                vCtx.rNeedBreak = YES;
                    
                    }];
                    
                    if (vCtx.NeedReturn())
                        return;
                    
                    vPrevParaIndex = vParaIndex;
                    
                }
                @finally {
                    DESTROY(vBlock);
                }
            } // for (; (vParaIndex.rParaIndex.block()
        }
        @finally {
            if (vPageRenderingNow) {
                [vPageRenderingNow setEndsWithPara: vParaIndex forDoc: self.Doc];
                // - записываем текущий конец незаконченной страницы
                if (!myRunnedOp &&
                    self.stopOnPageBreak &&
                    (vParaIndex.block() == nil))
                    myRunnedOp = YES;
                else
                if (!anOp || [anOp isCancelled])
                    [vPageRenderingNow dropRenderedTillEnd];
            }
            DESTROY(vPageRenderingNow);
            if ((anOp || self.isPart) /*&& ![anOp isCancelled]*/)
                [self save];
        }
    }
    @finally {
        [vPool drain];
        if (anOp && ![anOp isCancelled])
            [self.Doc pagesCounterDone];
    }
}