Информация об изменениях

Сообщение Re[96]: Мнение: объектно-ориентированное программирование — от 12.11.2019 4:02

Изменено 12.11.2019 7:16 Sinclair

Re[96]: Мнение: объектно-ориентированное программирование —
Здравствуйте, AlexRK, Вы писали:
ARK>То и значит. Ты ее не определишь никак, от слова совсем. Причем принципиально, фундаментально, не определишь. Можно взять одну и ту же программу на хаскеле и запустить ее сперва в виртуальной машине, которая выводит на принтер хоста каждое изменение регистров. А потом запустить в другой виртуальной машине, которая блокирует на хосте все запросы на ввод-вывод. В первом случае КАЖДАЯ функция в программе будет грязной. Во втором случае КАЖДАЯ функция в программе будет чистой. Вопрос: каковы же функции в этой программе, грязные или чистые? Программа одна и та же.
Погодите, погодите. А что, интуитивно очевидный способ уже не работает?
Делаем очень просто:
1. Функция является грязной, если она принимает IO среди своих аргументов.
2. Функция, вызывающая грязную, является грязной.
Всё. Все остальные функции — грязные.
А, ну можно ещё делать так:
2.1. Функция, возвращающая тот же IO, что передан в аргументах, является чистой.
Но это — факультативно.

Всё получается очень просто: к примеру, функция синуса является чистой ровно до тех пор, пока она работает только со своими аргументами:
public number sin(number x) => exp(ImaginaryOne()*x)-exp(-ImaginaryOne()*x))/2/ImaginaryOne();

Для того, чтобы знать, что она чистая, нам достаточно посмотреть на сигнатуру. Потому, что у неё нет доступа к IO, так что она не может зависеть ни от чего, кроме своих аргументов.
Чтобы испортить мир, ей нужна ссылка на мир:
public (number, IO) sin((number x, IO world)) => (exp(ImaginaryOne()*x)-exp(-ImaginaryOne()*x))/2/ImaginaryOne(), writeline(world, x));

Всё, мы сразу видим даже по сигнатуре, что sin согрешила. Теперь любая функция, захотевшая использовать синус, должна где-то взять IO, и тоже стать грязной.
Но! Мы можем построить функцию sinsquared двумя способами:
public (number, IO) sinsquared((number x, IO world))
{
  (var y, var world2)=sin(x, world);
  (var r, var world3)=sin(x, world2);
  return (r*y, world3);
});

public Function<IO, (number, IO)> sinsquared(number x)=>(IO world)=>
{
  (var y, var world2)=sin(x, world);
  (var r, var world3)=sin(x, world2);
  return (y*r, world3);
});

В первом случае мы получаем грязную функцию, т.к. она обращается к IO. Во втором — чистую, т.к. сама она к IO не обращается, но строит функцию, которая обращается к IO.

Разница между разными версиями синуса (ака "полезность определения чистой функции") — в том, как ей можно пользоваться.
Вот мы строили sinsquared — пришлось дважды вызывать синус с разными версиями мира. А для обычного, простого синуса, достаточно было бы
public number sinsquared(number x) => 
{
  var y = sin(x);
  return y*y;
}

Разница была бы ещё более заметна, попробуй мы обобщить понятие squared. Скормить "грязный" синус в функцию
public number square(Function<number, number> core, number x) => 
{
  var y = core(x);
  return y*y;
}

нам не удастся. Придётся делать специальную версию, умеющую возводить в квадрат грязные функции:
public (number, IO) squared(Function<(number, IO), (number, IO)> core, (number x, IO world))
{
  (var y, var world2)=core(x, world);
  (var r, var world3)=core(x, world2);
  return (r*y, world3);
});


Если бы мы работали с языком типа C, где мир доступен неявно — через static, global, и прочую аппаратуру, то нам пришлось бы подправить определения:
1. Грязной является функция, которая обращается "к миру".
2. Функция, вызывающая грязную, является грязной.
Всё, все остальные функции грязные. Разница между языками — в том, что теперь нам необходимо изучать исходники для понимания того, какая функция грязная, а какая — нет.
Альтернативой может стать включение ключевого слова pure в декларацию функции, а компилятор бы следил за выполнением п.1 и п.2.
Re[96]: Мнение: объектно-ориентированное программирование —
Здравствуйте, AlexRK, Вы писали:
ARK>То и значит. Ты ее не определишь никак, от слова совсем. Причем принципиально, фундаментально, не определишь. Можно взять одну и ту же программу на хаскеле и запустить ее сперва в виртуальной машине, которая выводит на принтер хоста каждое изменение регистров. А потом запустить в другой виртуальной машине, которая блокирует на хосте все запросы на ввод-вывод. В первом случае КАЖДАЯ функция в программе будет грязной. Во втором случае КАЖДАЯ функция в программе будет чистой. Вопрос: каковы же функции в этой программе, грязные или чистые? Программа одна и та же.
Погодите, погодите. А что, интуитивно очевидный способ уже не работает?
Делаем очень просто:
1. Функция является грязной, если она принимает IO среди своих аргументов.
2. Функция, вызывающая грязную, является грязной.
Всё. Все остальные функции — чистые. (Исправил оговорку)
А, ну можно ещё делать так:
2.1. Функция, возвращающая тот же IO, что передан в аргументах, является чистой.
Но это — факультативно.

Всё получается очень просто: к примеру, функция синуса является чистой ровно до тех пор, пока она работает только со своими аргументами:
public number sin(number x) => exp(ImaginaryOne()*x)-exp(-ImaginaryOne()*x))/2/ImaginaryOne();

Для того, чтобы знать, что она чистая, нам достаточно посмотреть на сигнатуру. Потому, что у неё нет доступа к IO, так что она не может зависеть ни от чего, кроме своих аргументов.
Чтобы испортить мир, ей нужна ссылка на мир:
public (number, IO) sin((number x, IO world)) => (exp(ImaginaryOne()*x)-exp(-ImaginaryOne()*x))/2/ImaginaryOne(), writeline(world, x));

Всё, мы сразу видим даже по сигнатуре, что sin согрешила. Теперь любая функция, захотевшая использовать синус, должна где-то взять IO, и тоже стать грязной.
Но! Мы можем построить функцию sinsquared двумя способами:
public (number, IO) sinsquared((number x, IO world))
{
  (var y, var world2)=sin(x, world);
  (var r, var world3)=sin(x, world2);
  return (r*y, world3);
});

public Function<IO, (number, IO)> sinsquared(number x)=>(IO world)=>
{
  (var y, var world2)=sin(x, world);
  (var r, var world3)=sin(x, world2);
  return (y*r, world3);
});

В первом случае мы получаем грязную функцию, т.к. она обращается к IO. Во втором — чистую, т.к. сама она к IO не обращается, но строит функцию, которая обращается к IO.

Разница между разными версиями синуса (ака "полезность определения чистой функции") — в том, как ей можно пользоваться.
Вот мы строили sinsquared — пришлось дважды вызывать синус с разными версиями мира. А для обычного, простого синуса, достаточно было бы
public number sinsquared(number x) => 
{
  var y = sin(x);
  return y*y;
}

Разница была бы ещё более заметна, попробуй мы обобщить понятие squared. Скормить "грязный" синус в функцию
public number square(Function<number, number> core, number x) => 
{
  var y = core(x);
  return y*y;
}

нам не удастся. Придётся делать специальную версию, умеющую возводить в квадрат грязные функции:
public (number, IO) squared(Function<(number, IO), (number, IO)> core, (number x, IO world))
{
  (var y, var world2)=core(x, world);
  (var r, var world3)=core(x, world2);
  return (r*y, world3);
});


Если бы мы работали с языком типа C, где мир доступен неявно — через static, global, и прочую аппаратуру, то нам пришлось бы подправить определения:
1. Грязной является функция, которая обращается "к миру".
2. Функция, вызывающая грязную, является грязной.
Всё, все остальные функции чистые. Разница между языками — в том, что теперь нам необходимо изучать исходники для понимания того, какая функция грязная, а какая — нет.
Альтернативой может стать включение ключевого слова pure в декларацию функции, а компилятор бы следил за выполнением п.1 и п.2.