Здравствуйте, samius, Вы писали:
S>Если на практике происходит специализация, то это не ПП, а ad-hoc.
Мы, вроде бы, сговорились на том, что особенности реализации не учитываются. Уже передумали?
S>(Потенциальная) — это значит что мне не потребуется специализировать для работы с еще одним любым типом.
Нет, не значит. "мне не потребуется специализировать для работы с еще одним любым типом" — это означает "конечное число".
S>А если мне в программе нужно всего 10 типов и не нужно бесконечность, то это не мои проблемы.
Правильно. А можно написать программу которой нужно не 10, а потенциально бесконечное число типов, как в обсуждаемых примерах — и привет, не компилируется!
S>А вы умудряетесь игнорировать то что определение ПП не требует бесконечности.
Я вообще не утверждал, что требует. Я утверждал, что бесконечность следует из определения ПП.
S>Но не в C#.
И в C#. Нет там никакого применения в рантайме. Вот применение параметрического типа в рантайме на C#:
typeof(Foo<>).MakeGenericType(new[]{typeof(Bar)})
А вот в комайл-тайм:
Foo<Bar>
K>>Но почему? S>Потому что кайндов в дотнете нет.
Спрашиваю: почему нет? Ответ — "потому что потому". Потрясающе просто!
S>Про бесконечное кол-во типов мы уже обсудили что оно не требуется определением.
Да, я вам несколько раз написал, что это следствие определения, но вы это просто проигнорировали. "Обсудили", конечно.
S>Полиморфизм — это фича языка, а не конкретной программы. Потому будет бесконечность типов в одной программе, или в разных — значения не имеет.
Т.е. предмет обсуждения значения не имеет. Отлично!
S>Тем более, что она не требуется и не следует.
Т.е. вы в состоянии дать верхнюю оценку числа "любых типов"? И верхнюю оценку числа типов в описываемых программах?
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, samius, Вы писали:
S>>Если на практике происходит специализация, то это не ПП, а ad-hoc.
K>Мы, вроде бы, сговорились на том, что особенности реализации не учитываются. Уже передумали?
Я лишь указал на то, что специализация (или выбор реализации в зависимости от типа) называется ad-hoc.
S>>(Потенциальная) — это значит что мне не потребуется специализировать для работы с еще одним любым типом.
K>Нет, не значит. "мне не потребуется специализировать для работы с еще одним любым типом" — это означает "конечное число".
Не означает, иначе вы его должны суметь оценить.
S>>А если мне в программе нужно всего 10 типов и не нужно бесконечность, то это не мои проблемы.
K>Правильно. А можно написать программу которой нужно не 10, а потенциально бесконечное число типов, как в обсуждаемых примерах — и привет, не компилируется!
Зато можно написать бесконечное множество программ, каждая из которых использует конечное число типов и компилируется.
S>>А вы умудряетесь игнорировать то что определение ПП не требует бесконечности.
K>Я вообще не утверждал, что требует. Я утверждал, что бесконечность следует из определения ПП.
А я утверждал что не следует. Как быть?
S>>Но не в C#.
K>И в C#. Нет там никакого применения в рантайме. Вот применение параметрического типа в рантайме на C#: K>
И ? Это действительно применение параметрического типа в рантайме. Противоречит тому что нет никакого применения в рантайме. K>А вот в комайл-тайм: K>
K>Foo<Bar>
K>
А вот это в какой тайм?
class X<T>
{
readonly X<Tuple<T>> _next;
public X(T v1, int count)
{
if (count > 0)
_next = new X<Tuple<T>>(Tuple.Create(v1), count - 1);
}
public Type GetTailType()
{
return _next == null
? GetType()
: _next.GetTailType();
}
}
class Program
{
static void Main()
{
int count = int.Parse(Console.ReadLine());
var x = new X<int>(1, count);
Console.WriteLine(x.GetTailType());
}
}
K>>>Но почему? S>>Потому что кайндов в дотнете нет.
K>Спрашиваю: почему нет? Ответ — "потому что потому". Потрясающе просто!
Так вы мне так же и отвечаете. Не хотелось бы начинать еще одну тему.
"потому что потому" получается после заглядывания в определение кайнда. Может вы под кайндом понимаете что-то другое, но тогда следовало дать свое определение.
S>>Про бесконечное кол-во типов мы уже обсудили что оно не требуется определением.
K>Да, я вам несколько раз написал, что это следствие определения, но вы это просто проигнорировали. "Обсудили", конечно.
А я вам несколько раз написал что 1) не следствие, что 2) бесконечность можно наблюдать на множестве программ.
S>>Полиморфизм — это фича языка, а не конкретной программы. Потому будет бесконечность типов в одной программе, или в разных — значения не имеет.
K>Т.е. предмет обсуждения значения не имеет. Отлично!
Вы обсуждаете полиморфизм или что? Если полиморфизм, то это фича языка, а не программы. Смотрим в определение. Если вы обсуждаете что-то другое, то я пойду.
S>>Тем более, что она не требуется и не следует. K>Т.е. вы в состоянии дать верхнюю оценку числа "любых типов"? И верхнюю оценку числа типов в описываемых программах?
К чему оценка? Доступное мне определение говорит лишь о возможности идентичной обработки значений вне зависимости от типа. Определение в TAPL не сильно от него отличается.
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
S>>Выделение памяти у нас превартилось в семантически обозримый side effect? С каких пор?
VE>Ты спрашиваешь, как программе определить, выделяется ли память?
Я спрашиваю не об этом. Если угодно, я спрашиваю о том, какой смысл говорить о чистоте чего-либо в рамках системы, где выделение памяти считается обозримым сайд-эффектом?
Можно, конечно, считать что вся доступная память передается неявным параметром в функцию, и неявным результатом возвращается, но такой подход оправдывает любые деструктивные изменения, а не только выделение. Так что, я считаю, что семантически-обозримое выделение памяти — это выход за рамки беседы.
VE>Математики не потому составляют чёткие определения, что они глупые, а как раз наоборот, потому что понимают, что дьявол в деталях. VE>Может, в определение напишем "side-effect — то, что неглупый человек считает side-effect'ом"?
Судя по всему, сейчас там так и написано. Во всяком случае каждый второй норовит его подправить своим определением.
S>>Ты путаешь referential transparency с детерминированностью. Если есть гарантия что результат будет тот же и не будет зависеть от ввода через I/O или скрытых состояний, то это есть детерминированность, а не ссылочная прозрачность. Детерминированной функции портить мир можно. Ссылочно прозрачному выражению — нельзя.
VE>Не путаю. Она и не портит, мир живёт себе как жил. Ничем не хуже, чем с выделенной памятью.
По поводу выделения памяти ответил выше.
VE>>>Что мешает использовать файл как оперативную память? S>>Ничего. Только детерминированности не будет, т.к. общаешься с файлом через I/O. А нет детерминированности — нет и прозрачности.
VE>Почему это не будет? На n! при сотне вызовов возвращает верное значение? Да. Так что ж тогда?
Определение детерминированности гласит что результат кроме гарантированного повтора на тех же арргументах, не должен зависеть от ввода. Чтение файла — это ввод. Надеюсь, что этим самым мы закрываем тему о детерминированности чтения из файла.
VE>>>Нет, ты со мной не согласен. Я задал кучу вопросов, на которые такое определение ответить не в состоянии. S>>Мне казалось, что я на все ответил, опираясь на определение.
VE>Это тебе казалось, а я ответов не получил. Видимо потому, что ты тоже опираешься на "интуицию умных людей", а у меня такое понятие отсутствует, мне нужно чёткое определение, чтоб определение было полезным даже для дурака вроде меня. Эдак можно начать опираться на клятвы матерью и своим здоровьем.
Извини, у меня нет определения, которое бы дало совершенно четкие указания о том, считать ли нагрев процессора/разгон вентилятора и т.п. побочным эффектом. Кроме того, уверен, что если бы оно было, ты бы что-нибудь придумал.
VE>А что если работает рантайм, но программа имеет возможность косвенно следить за её работой? Ну, как с памятью.
По поводу памяти ответил выше. Только я не понимаю, к чему тебе слежение за памятью или голубями? Начинай следить сразу за регистрами CPU. Так проще. Чистота сразу превращается в миф и теряет практическую актуальность.
VE>Или обратный эффект. Что, если я могу рантайм расширять? Ну вот например ввёл доставки голубем, и с т.з. языка это считается runtime extension и даёт те же гарантии, что и работа с памятью. А дальше компилятор на основе метаданных выбирает реализацию сам — то ли память, то ли голуби.
Даже если сам Евклид будет тебе вычислять факториал на пергаменте по тексту программы, к сайд эффектам самой программы, которая явно не изменяет состояние самой себя и окружающего мира, это отношения иметь не будет.
VE>Я не думаю, что это такой уж надуманный пример, вон взять Nemerle 2, который будет как бы инструмент для создания языков. Там может понадобиться нечто подобное. В базовом языке (который ещё и грязный) у нас будет всё превращаться в голубиную почту, но в исходном чистом ДСЛ это будет обычное вычисление.
Так обычно и бывает с чистыми языками, N2 тут ничего не открыл.
S>>Посылка данных в сеть — это output через I/O девайс. А выделение памяти не является семантически обозримым сайд эффектом.
VE>В чём разница? Увидеть можно и то, и другое. Изнутри программы.
Разница в том, что одно считается вводом/выводом, а другое — нет.
S>>Что по поводу insignificant сайд эффекта — так это представь вычисление факториала со внешним мутабельным аккумулятором. Такая функция будет формально impure, но включенная в выражение таким образом, что бы изменения не распространялись за пределы выражения, ее побочный эффект будет незначителен для того, кто ожидает результат выражения.
VE>Можно ещё представить себе вычисление огроменного списка. Формально он pure, но запусти их 3 сразу и памяти не хватит. И это оказывается очень так значительно для того, кто ожидает результат вычисления. Но это лирика.
Конечно лирика. Если мы начнем сравнивать кол-во доступной памяти до и после вызова, то вся чистота будет лирикой, не нужно и одновременного запуска 3х вычислений.
VE>А можно опять вернуться к голубиной почте, а лучше к temporary файл. Можно создать temporary file (имя которому генерирует ОСь, и имя которого неизвестно никому, только handle открывшему процессу), поиспользовать для вычислений и закрыть (после чего он может удалиться, а может и нет, зависит от флагов открытия). Такая функция чиста? Вроде ввод-вывод есть, но побочный эффект необнаруживаем.
Определение требует считать такую функцию грязной, ибо ввод/вывод. Но ты можешь считать ее достаточно чистой для каких-то своих соображений. Можешь даже убедить в этом haskell через unsafePerformIO. Только не жалуйся, что для выполнения чистой функции потребовались права.
Здравствуйте, samius, Вы писали:
K>>Нет, не значит. "мне не потребуется специализировать для работы с еще одним любым типом" — это означает "конечное число". S>Не означает, иначе вы его должны суметь оценить.
Я тут уже указывал способ определить не верхнюю границу, а точное число типов для тизируемого кода на C++
S>Зато можно написать бесконечное множество программ, каждая из которых использует конечное число типов и компилируется.
Но типизируется-то конкретная программа, а не "множество программ". И потенциальная бесконечность следует для одной программы.
S>А я утверждал что не следует. Как быть?
Из определения парам. полиморфизма следует, что у нас есть, по крайней мере, один мономорфный тип () и один полиморфный forall a. Succ a с этим вы согласны? Тогда из любого заданного типа a мы можем сконструировать еще один применением Succ. Таким образом, число типов сверху не ограничено. Q.E.D
Ровно это следствие в приведенных тут программах на языках с ПП и используется.
S>>>Но не в C#.
S>Это действительно применение параметрического типа в рантайме. Противоречит тому что нет никакого применения в рантайме.
Речь шла о том, что применение в рантайме для написания обсуждаемого кода не требуется.
S>А вот это в какой тайм? S>
S> class X<T>
S> {
S> readonly X<Tuple<T>> _next;
S> public X(T v1, int count)
S> {
S> if (count > 0)
S> _next = new X<Tuple<T>>(Tuple.Create(v1), count - 1);
S> }
S> public Type GetTailType()
S> {
S> return _next == null
S> ? GetType()
S> : _next.GetTailType();
S> }
S> }
S> class Program
S> {
S> static void Main()
S> {
S> int count = int.Parse(Console.ReadLine());
S> var x = new X<int>(1, count);
S> Console.WriteLine(x.GetTailType());
S> }
S> }
S>
Компайл-тайм, разумеется.
S>Так вы мне так же и отвечаете. Не хотелось бы начинать еще одну тему. S>"потому что потому" получается после заглядывания в определение кайнда. Может вы под кайндом понимаете что-то другое, но тогда следовало дать свое определение.
Мое определение, разумеется, не отличается от википедийного. Не понял, как вы из него вывели, что в .net нет кайндов? В примерах из статьи, правда, есть фактические ошибки. Например, кайнд (->) в хаскеле не * -> * -> *, а ?? -> ? -> *. Прямые аналоги хаскельных кайндов # * в .net это struct и class, и полиморфизм в .net работает, в отличие от хаскеля, для кайнда, который в хаскеле называется ??. Домашнее задание — к типам каких кайндов можно применять оператор typeof в C#?
S>А я вам несколько раз написал что 1) не следствие, что 2) бесконечность можно наблюдать на множестве программ.
См. доказательство выше.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, samius, Вы писали:
S>Можно, конечно, считать что вся доступная память передается неявным параметром в функцию, и неявным результатом возвращается, но такой подход оправдывает любые деструктивные изменения, а не только выделение. Так что, я считаю, что семантически-обозримое выделение памяти — это выход за рамки беседы.
Неявным нельзя, явным — пожалуйста.
foo :: Ptr Int -> Ptr Int
очень явно отличается от
foo :: Memory -> Ptr Int -> (Ptr Int, Memory)
Вторая — чистая. Вы не можете вычислить две foo, кроме как последовательно их соединив, и в этом смысле они referential transparent.
Для иллюстрации вы можете имплементировать Ptr как Int, а Memory как [Byte].
S>Определение детерминированности гласит что результат кроме гарантированного повтора на тех же арргументах, не должен зависеть от ввода. Чтение файла — это ввод. Надеюсь, что этим самым мы закрываем тему о детерминированности чтения из файла.
Не закроем. Фукнция создаёт файл с уникальным доступом, пишет, читает, в конце возвращает результат. Результат будет неизменен, хоть 100 раз вызови.
По поводу памяти, видел программу artmoney? Находишь адрес памяти, отвечающий за кол-во жизней в игре, ставишь 100. Ну и кто же более детерминирован тут, файл или память?
S>Извини, у меня нет определения, которое бы дало совершенно четкие указания о том, считать ли нагрев процессора/разгон вентилятора и т.п. побочным эффектом. Кроме того, уверен, что если бы оно было, ты бы что-нибудь придумал.
Именно. Поэтому это определение смысла не имеет.
S>По поводу памяти ответил выше. Только я не понимаю, к чему тебе слежение за памятью или голубями? Начинай следить сразу за регистрами CPU. Так проще. Чистота сразу превращается в миф и теряет практическую актуальность.
Как раз referential transparency от этого никуда не девается, а про чистоту я уже не раз сказал, что она не имеет смысла.
S>Даже если сам Евклид будет тебе вычислять факториал на пергаменте по тексту программы, к сайд эффектам самой программы, которая явно не изменяет состояние самой себя и окружающего мира, это отношения иметь не будет.
Ну если в самой программе написано "позови Софокла, пусть он второй пергамент порешает", то очень даже имеет. Но результат всё равно детерминирован.
VE>>В чём разница? Увидеть можно и то, и другое. Изнутри программы. S>Разница в том, что одно считается вводом/выводом, а другое — нет.
Одно является I/O потому, что оно считается I/O, а другое нет — потому что нет. Весьма полезное определение.
S>Определение требует считать такую функцию грязной, ибо ввод/вывод. Но ты можешь считать ее достаточно чистой для каких-то своих соображений. Можешь даже убедить в этом haskell через unsafePerformIO. Только не жалуйся, что для выполнения чистой функции потребовались права (_|_).
Ты можешь считать достаточно чистой функцию вычисления суммы списка чисел. Можешь даже убедить в этом меня. Только не жалуйся, что для выполнения чистой функции не хватило памяти (_|_).
Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, samius, Вы писали:
K>>>Нет, не значит. "мне не потребуется специализировать для работы с еще одним любым типом" — это означает "конечное число". S>>Не означает, иначе вы его должны суметь оценить.
K>Я тут уже указывал способ определить не верхнюю границу, а точное число типов для тизируемого кода на C++
Остается выяснить, что же мешает превзойти заранее заданное точное число типов.
S>>Зато можно написать бесконечное множество программ, каждая из которых использует конечное число типов и компилируется.
K>Но типизируется-то конкретная программа, а не "множество программ".
Но мы обсуждаем ПП, фичу языка, а не конкретную программу. Причем тут конкретная программа — непонятно.
K>И потенциальная бесконечность следует для одной программы.
Нет.
S>>А я утверждал что не следует. Как быть?
K>Из определения парам. полиморфизма следует, что у нас есть, по крайней мере, один мономорфный тип () и один полиморфный forall a. Succ a с этим вы согласны?
Нет. Из определения ПП следует лишь то, что такие типы полиморфным кодом должны обрабатываться единым образом. А то что они должны существовать, да еще и в какой-то конкретной программе, да еще и потенциальной бесконечностью — увы, не следует.
K>Тогда из любого заданного типа a мы можем сконструировать еще один применением Succ. Таким образом, число типов сверху не ограничено. Q.E.D
что мешает сконструитьвать еще один тип в программе на C++? Разумеется, явно в коде, или в компайл-тайме.
K>Ровно это следствие в приведенных тут программах на языках с ПП и используется.
Нету такого следствия.
S>>Это действительно применение параметрического типа в рантайме. Противоречит тому что нет никакого применения в рантайме.
K>Речь шла о том, что применение в рантайме для написания обсуждаемого кода не требуется.
Если обсуждаются примеры из http://migmit.livejournal.com/32688.html, то С# там их применяет в рантайме.
S>>А вот это в какой тайм? S>>
S>> int count = int.Parse(Console.ReadLine());
S>> var x = new X<int>(1, count);
S>>
K>Компайл-тайм, разумеется.
Тогда вопрос. Откуда C# в компайл тайм знает что надо применить как минимум 1000 типов? Для 1000 код работает, мог бы работать и для большего числа, если бы не StackOverflow.
S>>"потому что потому" получается после заглядывания в определение кайнда. Может вы под кайндом понимаете что-то другое, но тогда следовало дать свое определение.
K>Мое определение, разумеется, не отличается от википедийного. Не понял, как вы из него вывели, что в .net нет кайндов? В примерах из статьи, правда, есть фактические ошибки. Например, кайнд (->) в хаскеле не * -> * -> *, а ?? -> ? -> *. Прямые аналоги хаскельных кайндов # * в .net это struct и class, и полиморфизм в .net работает, в отличие от хаскеля, для кайнда, который в хаскеле называется ??. Домашнее задание — к типам каких кайндов можно применять оператор typeof в C#?
В случае с кайндами — виноват, признаю свою ошибку.
Д.З. Если я правильно все понял, то typeof разрешен для
#
*
(?,...)->? -- unbounded type name
В последнем случае — подразумевается то, что typeof(Dictionary<,>) можно, а typeof(Dictionary<int, >) нельзя, т.е. никаких частичных применений.
А так же можно для указателей (но не ссылок 'out/ref'), которые я не знаю как обозначить в нотации кайндов хаскеля;
и, конечно, Void.
Может быть еще для чего-то можно.
S>>А я вам несколько раз написал что 1) не следствие, что 2) бесконечность можно наблюдать на множестве программ.
K>См. доказательство выше.
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
S>>Можно, конечно, считать что вся доступная память передается неявным параметром в функцию, и неявным результатом возвращается, но такой подход оправдывает любые деструктивные изменения, а не только выделение. Так что, я считаю, что семантически-обозримое выделение памяти — это выход за рамки беседы.
VE>foo :: Memory -> Ptr Int -> (Ptr Int, Memory) VE>Вторая — чистая. Вы не можете вычислить две foo, кроме как последовательно их соединив, и в этом смысле они referential transparent. VE>Для иллюстрации вы можете имплементировать Ptr как Int, а Memory как [Byte].
Вот что в этом аспекте интересно, а почему в хаскеле любую функцию (например, конкретно map) не оформили в виде монады Memory a?
S>>Определение детерминированности гласит что результат кроме гарантированного повтора на тех же арргументах, не должен зависеть от ввода. Чтение файла — это ввод. Надеюсь, что этим самым мы закрываем тему о детерминированности чтения из файла.
VE>Не закроем. Фукнция создаёт файл с уникальным доступом, пишет, читает, в конце возвращает результат. Результат будет неизменен, хоть 100 раз вызови.
А ввод? Детерминированность это не только гарантия повторения результата. Уже устал повторять. VE>По поводу памяти, видел программу artmoney? Находишь адрес памяти, отвечающий за кол-во жизней в игре, ставишь 100. Ну и кто же более детерминирован тут, файл или память?
Мне непонятно что значит "более детерминирован". Результат, завязанный на работу с файлом недетерминирован по определению. С памятью — зависит от остального.
S>>Извини, у меня нет определения, которое бы дало совершенно четкие указания о том, считать ли нагрев
VE>Именно. Поэтому это определение смысла не имеет.
А какой смысл в хаскеле делать вид что его функции чисты? Или он как-то с памятью по-особому работает?
S>>По поводу памяти ответил выше. Только я не понимаю, к чему тебе слежение за памятью или голубями? Начинай следить сразу за регистрами CPU. Так проще. Чистота сразу превращается в миф и теряет практическую актуальность.
VE>Как раз referential transparency от этого никуда не девается, а про чистоту я уже не раз сказал, что она не имеет смысла.
referential transparency определяется через те же эффекты, что и чистота. Непонятно, что вдруг чистота куда-то девается, а referential transparency, определенная на той же детерминированности — нет.
S>>Даже если сам Евклид будет тебе вычислять факториал на пергаменте по тексту программы, к сайд эффектам самой программы, которая явно не изменяет состояние самой себя и окружающего мира, это отношения иметь не будет.
VE>Ну если в самой программе написано "позови Софокла, пусть он второй пергамент порешает", то очень даже имеет. Но результат всё равно детерминирован.
"позови Софокла, пусть он порешает" — не ввод вывод. А вот "спроси Софокла, чему будет равен результат" — ввод вывод.
VE>>>В чём разница? Увидеть можно и то, и другое. Изнутри программы. S>>Разница в том, что одно считается вводом/выводом, а другое — нет.
VE>Одно является I/O потому, что оно считается I/O, а другое нет — потому что нет. Весьма полезное определение.
Оно по крайней мере допускает наличие чистых функций в хаскеле (кроме действий). Твои же определения — наоборот, говорят что лишь действия IO a чисты (ну и может быть еще что-то, что не оперирует санками).
VE>Ты можешь считать достаточно чистой функцию вычисления суммы списка чисел. Можешь даже убедить в этом меня. Только не жалуйся, что для выполнения чистой функции не хватило памяти (_|_).
Дык это нормально. Чистой функции никто не запрещает жрать память. Портить левую — нельзя. А жрать — сколько влезет.
Здравствуйте, samius, Вы писали:
VE>>foo :: Memory -> Ptr Int -> (Ptr Int, Memory) VE>>Вторая — чистая. Вы не можете вычислить две foo, кроме как последовательно их соединив, и в этом смысле они referential transparent. VE>>Для иллюстрации вы можете имплементировать Ptr как Int, а Memory как [Byte].
S>Вот что в этом аспекте интересно, а почему в хаскеле любую функцию (например, конкретно map) не оформили в виде монады Memory a?
Потому же, почему и runST не надо оформлять в виде монады IO — referential transparency.
Потому же, почему и withTemporaryFile не нужно оформлять в виде монады IO — referential transparency.
Приведённая же функция foo с деструктивным присваиванием — в общем случае грязная.
Если от withTemporaryFile вам нужен именно её "побочный эффект" в виде ввода-вывода, язык должен позволять это указать, тогда это будет IO функция с вводом-выводом и выполняться будет каждый раз.
Что именно важно: результат функции (полученный как угодно) или её эффект — определять должен программист.
Ситуация чем-то напоминает мне вопрос о том, "исключительная ли ситуация, если openFile не смог открыть файл?"
Файл-то чёрт с ним, но вот parse и tryParse существуют прямо здесь и прямо сейчас. А ещё всякие foo и fooAsync.
Вакханалия какая-то.
VE>>Не закроем. Фукнция создаёт файл с уникальным доступом, пишет, читает, в конце возвращает результат. Результат будет неизменен, хоть 100 раз вызови. S>А ввод? Детерминированность это не только гарантия повторения результата. Уже устал повторять.
А я устал повторять, что чтение памяти ничем не отличается от чтения файла, кроме того, что одно IO, потому что считается IO, а другое не IO, потому что таковым не считается.
S>Мне непонятно что значит "более детерминирован". Результат, завязанный на работу с файлом недетерминирован по определению. С памятью — зависит от остального.
По определению "недетерминирован, потому что считается таковым". Это не определение, а чушь какая-то.
S>>>Извини, у меня нет определения, которое бы дало совершенно четкие указания о том, считать ли нагрев
VE>>Именно. Поэтому это определение смысла не имеет. S>А какой смысл в хаскеле делать вид что его функции чисты? Или он как-то с памятью по-особому работает?
Они referential trasparent. unsafePerformIO видел? Так вот она unsafe (как и unsafeCoerce) потому, что ответственность лежит на программисте.
S>referential transparency определяется через те же эффекты, что и чистота. Непонятно, что вдруг чистота куда-то девается, а referential transparency, определенная на той же детерминированности — нет.
Не через те же.
An expression is said to be referentially transparent if it can be replaced with its value without changing the behavior of a program (in other words, yielding a program that has the same effects and output on the same input).
Я настаиваю на том, что если вы считаете идентичными программы, одна из которых выделяет памяти до гигабайта, а другая до мегабайта (потому что в ней что-то там замемоизовали благодаря referential trasparent), то можно считать идентичными и те, одна из которых 3 раза создаёт использует с нуля временный файл, а другая лишь один (потому что можно работу с файлом мемоизовали).
Более того, я считаю, что важность эффекта определяется не википедией, а программистом.
Т.е. конкретно в данном примере я считаю, что первые две программы по эффектам отличаются сильнее, чем вторые две.
Кстати говоря, вы что, никогда не видели ситуации, как мемоизуют функции с побочными эффектами (в вашем определении)? Получается то же самое. Ну да, ну да, там же этого программист захотел. А в отношении withTemporaryFile мне запрещает этого хотеть википедия?
S>Оно по крайней мере допускает наличие чистых функций в хаскеле (кроме действий). Твои же определения — наоборот, говорят что лишь действия IO a чисты (ну и может быть еще что-то, что не оперирует санками).
Мои определения ничего такого не говорят.
VE>>Ты можешь считать достаточно чистой функцию вычисления суммы списка чисел. Можешь даже убедить в этом меня. Только не жалуйся, что для выполнения чистой функции не хватило памяти (_|_). S>Дык это нормально. Чистой функции никто не запрещает жрать память. Портить левую — нельзя. А жрать — сколько влезет.
И какую такую левую функцию портит withTemporaryFile, которой не хватило прав?
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
VE>>>foo :: Memory -> Ptr Int -> (Ptr Int, Memory) VE>>>Вторая — чистая. Вы не можете вычислить две foo, кроме как последовательно их соединив, и в этом смысле они referential transparent. VE>>>Для иллюстрации вы можете имплементировать Ptr как Int, а Memory как [Byte].
S>>Вот что в этом аспекте интересно, а почему в хаскеле любую функцию (например, конкретно map) не оформили в виде монады Memory a?
VE>Потому же, почему и runST не надо оформлять в виде монады IO — referential transparency. VE>Потому же, почему и withTemporaryFile не нужно оформлять в виде монады IO — referential transparency.
А разве referential transparency не чушь? Она не сохраняет нагрев процессора
VE>Приведённая же функция foo с деструктивным присваиванием — в общем случае грязная.
А что по поводу грязной функции map без деструктивного присваивания?
VE>Если от withTemporaryFile вам нужен именно её "побочный эффект" в виде ввода-вывода, язык должен позволять это указать, тогда это будет IO функция с вводом-выводом и выполняться будет каждый раз.
не знаю, что это
VE>Что именно важно: результат функции (полученный как угодно) или её эффект — определять должен программист.
велкам в С++
VE>Ситуация чем-то напоминает мне вопрос о том, "исключительная ли ситуация, если openFile не смог открыть файл?" VE>Файл-то чёрт с ним, но вот parse и tryParse существуют прямо здесь и прямо сейчас. А ещё всякие foo и fooAsync. VE>Вакханалия какая-то.
что за parse и tryParse?
VE>>>Не закроем. Фукнция создаёт файл с уникальным доступом, пишет, читает, в конце возвращает результат. Результат будет неизменен, хоть 100 раз вызови. S>>А ввод? Детерминированность это не только гарантия повторения результата. Уже устал повторять.
VE>А я устал повторять, что чтение памяти ничем не отличается от чтения файла, кроме того, что одно IO, потому что считается IO, а другое не IO, потому что таковым не считается.
от того что ты устал считаться по-другому не станет.
S>>Мне непонятно что значит "более детерминирован". Результат, завязанный на работу с файлом недетерминирован по определению. С памятью — зависит от остального.
VE>По определению "недетерминирован, потому что считается таковым". Это не определение, а чушь какая-то.
От того что ты называешь определения чушью, других определений не появляется. Предлагаю все-таки ссылаться на какие-то источники, а не на "я так считаю".
S>>А какой смысл в хаскеле делать вид что его функции чисты? Или он как-то с памятью по-особому работает?
VE>Они referential trasparent. unsafePerformIO видел? Так вот она unsafe (как и unsafeCoerce) потому, что ответственность лежит на программисте.
То есть все, кто пишут что хаскель чист — пишут чушь?
S>>referential transparency определяется через те же эффекты, что и чистота. Непонятно, что вдруг чистота куда-то девается, а referential transparency, определенная на той же детерминированности — нет.
VE>Не через те же.
VE>An expression is said to be referentially transparent if it can be replaced with its value without changing the behavior of a program (in other words, yielding a program that has the same effects and output on the same input).
VE>Я настаиваю на том, что если вы считаете идентичными программы, одна из которых выделяет памяти до гигабайта, а другая до мегабайта (потому что в ней что-то там замемоизовали благодаря referential trasparent), то можно считать идентичными и те, одна из которых 3 раза создаёт использует с нуля временный файл, а другая лишь один (потому что можно работу с файлом мемоизовали).
идентичными? Нигде такого не писал. А как же нагрев процессора? Он делает RT чушью
VE>Более того, я считаю, что важность эффекта определяется не википедией, а программистом.
даёшь всё IO без монады IO!
VE>Т.е. конкретно в данном примере я считаю, что первые две программы по эффектам отличаются сильнее, чем вторые две.
окей, я это уже понял. Так же как и то, что это плохо соотносится с другими источниками об эффектах.
VE>Кстати говоря, вы что, никогда не видели ситуации, как мемоизуют функции с побочными эффектами (в вашем определении)? Получается то же самое. Ну да, ну да, там же этого программист захотел. А в отношении withTemporaryFile мне запрещает этого хотеть википедия?
хотеть не запрещает
S>>Оно по крайней мере допускает наличие чистых функций в хаскеле (кроме действий). Твои же определения — наоборот, говорят что лишь действия IO a чисты (ну и может быть еще что-то, что не оперирует санками).
VE>Мои определения ничего такого не говорят.
Мне показалось что они указывают на то что выделение памяти есть семантически обозримый побочный эффект.
S>>Дык это нормально. Чистой функции никто не запрещает жрать память. Портить левую — нельзя. А жрать — сколько влезет.
VE>И какую такую левую функцию портит withTemporaryFile, которой не хватило прав?
я ничего не утверждал о порче функций. Я написал что выделять память чистой функции не запрещено.
Здравствуйте, samius, Вы писали:
VE>>Потому же, почему и runST не надо оформлять в виде монады IO — referential transparency. VE>>Потому же, почему и withTemporaryFile не нужно оформлять в виде монады IO — referential transparency. S>А разве referential transparency не чушь? Она не сохраняет нагрев процессора
И не должна. RT позволяет заменить вычисление значением без смены поведения программы. Поведение программы определяет программист.
Т.е. если должна была греть процессор и перестала — то вызов функции, греющую процессор, заменить на значение нельзя. Если пофиг, греет или нет, то можно менять, и функция RT. Это относительное понятие, а не абсолютное.
S>А что по поводу грязной функции map без деструктивного присваивания?
Какой такой грязной map?
VE>>Если от withTemporaryFile вам нужен именно её "побочный эффект" в виде ввода-вывода, язык должен позволять это указать, тогда это будет IO функция с вводом-выводом и выполняться будет каждый раз. S>не знаю, что это
withTemporaryFile :: (Handle -> IO r) -> r
Если вы используете её так же, как runST, она вполне себе RT. Если вам необходимо именно создание временного файла, то необходим аналог stToIO.
Что такое runST знаете?
VE>>Что именно важно: результат функции (полученный как угодно) или её эффект — определять должен программист. S>велкам в С++
Это такой язык, где гипотетически можно сделать всё (и некоторые даже воротят монстров), но на деле никто ничего такого не использует, потому что без слёз не взглянешь? Спасибо, знаком.
VE>>Ситуация чем-то напоминает мне вопрос о том, "исключительная ли ситуация, если openFile не смог открыть файл?" VE>>Файл-то чёрт с ним, но вот parse и tryParse существуют прямо здесь и прямо сейчас. А ещё всякие foo и fooAsync. VE>>Вакханалия какая-то. S>что за parse и tryParse?
System.Int.parse и System.Int.tryParse (.Net)
VE>>По определению "недетерминирован, потому что считается таковым". Это не определение, а чушь какая-то. S>От того что ты называешь определения чушью, других определений не появляется. Предлагаю все-таки ссылаться на какие-то источники, а не на "я так считаю".
Можно конечно за абсолют принять википедию и годами всех убеждать, что она верна, а думать на эту тему — время терять, а можно таки самому думать над вопросом. Глядишь, и определение в вики исправят.
S>>>А какой смысл в хаскеле делать вид что его функции чисты? Или он как-то с памятью по-особому работает?
VE>>Они referential trasparent. unsafePerformIO видел? Так вот она unsafe (как и unsafeCoerce) потому, что ответственность лежит на программисте. S>То есть все, кто пишут что хаскель чист — пишут чушь?
Я тут раз десять сказал, что понятие "чистый" бесполезное, а ты меня спрашиваешь, чистый ли Хаскель. Ну, с т.з. википедии чистый, но меня это не волнует.
VE>>Я настаиваю на том, что если вы считаете идентичными программы, одна из которых выделяет памяти до гигабайта, а другая до мегабайта (потому что в ней что-то там замемоизовали благодаря referential trasparent), то можно считать идентичными и те, одна из которых 3 раза создаёт использует с нуля временный файл, а другая лишь один (потому что можно работу с файлом мемоизовали). S>идентичными? Нигде такого не писал. А как же нагрев процессора? Он делает RT чушью
Поведение программы — это не её проявление на одном каком-то компьютере, а заложенное программистом. То, что на одной системе она греет процессор, на другой моргает лампочками — в поведение не включается.
Если я пишу программу, которая именно что должна греть процессор, то да, вычисление списка не будет RT. Если я пишу калькулятор, то греет он процессор, открывает ли временные файлы, подключается ли к базе — это всё не важно. Если поведение от замены функций на значение (калькуляторное) не меняется, функции — RT.
VE>>Более того, я считаю, что важность эффекта определяется не википедией, а программистом. S>даёшь всё IO без монады IO!
Не всё, а только то, которое является лишь побочным продуктом и не влияет на результат. С putStrLn это, очевидно, не так. putStrLn — ввод-вывод.
withNewIORef (withNewIORef :: a -> (IORef a -> IO b) -> b — не ввод-вывод).
VE>>Кстати говоря, вы что, никогда не видели ситуации, как мемоизуют функции с побочными эффектами (в вашем определении)? Получается то же самое. Ну да, ну да, там же этого программист захотел. А в отношении withTemporaryFile мне запрещает этого хотеть википедия? S>хотеть не запрещает
Ок, пойдём дальше. Вот пишите вы себе на C++ мемоизатор. И что, поведение программы меняется? Ну т.е. вроде бы что-то меняется, как если одну сортировку на другую поменять, но вот поведение — не меняется. Так как время — не заложено в поведении.
VE>>Мои определения ничего такого не говорят. S>Мне показалось что они указывают на то что выделение памяти есть семантически обозримый побочный эффект.
А также на то, что является ли это поведением или просто implementation defined (т.е. программе в целом насрать на это), так сказать, определяет программист.
S>>>Дык это нормально. Чистой функции никто не запрещает жрать память. Портить левую — нельзя. А жрать — сколько влезет.
VE>>И какую такую левую функцию портит withTemporaryFile, которой не хватило прав? S>я ничего не утверждал о порче функций. Я написал что выделять память чистой функции не запрещено.
Не запрещено Абсолютом.
Вот эту фразу поясните:
Дык это нормально. Чистой функции никто не запрещает жрать память. Портить левую — нельзя. А жрать — сколько влезет.
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
VE>>>Потому же, почему и runST не надо оформлять в виде монады IO — referential transparency. VE>>>Потому же, почему и withTemporaryFile не нужно оформлять в виде монады IO — referential transparency. S>>А разве referential transparency не чушь? Она не сохраняет нагрев процессора
VE>И не должна. RT позволяет заменить вычисление значением без смены поведения программы. Поведение программы определяет программист.
"(in other words, yielding a program that has the same effects and output on the same input)"
VE>Т.е. если должна была греть процессор и перестала — то вызов функции, греющую процессор, заменить на значение нельзя. Если пофиг, греет или нет, то можно менять, и функция RT. Это относительное понятие, а не абсолютное.
Про нагрев процессора можно сказать то же самое и в плане чистоты и побочных эффектов. Нагрев процессора, так же как и изменения в регистрах/кэшах и т.п., не считаются побочным эффектом (конечно, если не влияют на результат вычислений других программ).
S>>А что по поводу грязной функции map без деструктивного присваивания? VE>Какой такой грязной map?
Тот самый map, который возвращает результат, выделяя память под него. Ты ведь предложил считать выделение памяти обозримым побочным эффектом, вот потому map стал грязным в тот же час.
VE>>>Если от withTemporaryFile вам нужен именно её "побочный эффект" в виде ввода-вывода, язык должен позволять это указать, тогда это будет IO функция с вводом-выводом и выполняться будет каждый раз. S>>не знаю, что это
VE>
VE>withTemporaryFile :: (Handle -> IO r) -> r
VE>
Как я понимаю, такая сигнатура не мешает нагадить не только в этот файл, а вообще везде где взбредет.
VE>Если вы используете её так же, как runST, она вполне себе RT. Если вам необходимо именно создание временного файла, то необходим аналог stToIO. VE>Что такое runST знаете?
Еще нет. Но во всяком случае, об этом есть что почитать. А по поводу withTemporaryFile гугл молчит.
S>>что за parse и tryParse?
VE>System.Int.parse и System.Int.tryParse (.Net)
Не признал. Не тот регистр, да и типов System.Int в дотнете нет. Теперь понял, о чем речь. Согласен, не лучшее решение. Но когда не знаешь, как бывает по-другому, претензий не возникает, хотя чувство дискомфорта присутствует.
VE>Можно конечно за абсолют принять википедию и годами всех убеждать, что она верна, а думать на эту тему — время терять, а можно таки самому думать над вопросом. Глядишь, и определение в вики исправят.
Не вижу повода для его исправления. А, главное, не понятно, какое определение лучше и чем.
S>>То есть все, кто пишут что хаскель чист — пишут чушь?
VE>Я тут раз десять сказал, что понятие "чистый" бесполезное, а ты меня спрашиваешь, чистый ли Хаскель. Ну, с т.з. википедии чистый, но меня это не волнует.
Нет, не это спрашиваю. По этому поводу у меня свое мнение и пока никто меня не разубедил. Я спрашиваю о том, считаешь ли ты что все кто пишет что хаскель чист, пишут чушь?
VE>>>Я настаиваю на том, что если вы считаете идентичными программы, одна из которых выделяет памяти до гигабайта, а другая до мегабайта (потому что в ней что-то там замемоизовали благодаря referential trasparent), то можно считать идентичными и те, одна из которых 3 раза создаёт использует с нуля временный файл, а другая лишь один (потому что можно работу с файлом мемоизовали). S>>идентичными? Нигде такого не писал. А как же нагрев процессора? Он делает RT чушью
VE>Поведение программы — это не её проявление на одном каком-то компьютере, а заложенное программистом. То, что на одной системе она греет процессор, на другой моргает лампочками — в поведение не включается.
В побочные эффекты тоже VE>Если я пишу программу, которая именно что должна греть процессор, то да, вычисление списка не будет RT. Если я пишу калькулятор, то греет он процессор, открывает ли временные файлы, подключается ли к базе — это всё не важно. Если поведение от замены функций на значение (калькуляторное) не меняется, функции — RT.
Вижу, что твоя RT тоже устроена по понятиям. Файлы и БД не считаются единым целым с программой. Чтение из них — это ввод. Если то, что ты прочитаешь из файла/БД будет влиять на результат вычисления, то его нельзя классифицировать как RT.
VE>>>Более того, я считаю, что важность эффекта определяется не википедией, а программистом. S>>даёшь всё IO без монады IO!
VE>Не всё, а только то, которое является лишь побочным продуктом и не влияет на результат. С putStrLn это, очевидно, не так. putStrLn — ввод-вывод.
Ура, хоть в чем-то согласие. Только поправлю, putStrLn — это не ввод-вывод, ввод-вывод — это будет IO(), который она вернет. VE>withNewIORef (withNewIORef :: a -> (IORef a -> IO b) -> b — не ввод-вывод).
Нет? А что мне помешает подсунуть в withNewIORef экшн, возвращаемый putStrLn (который ввод-вывод)? Просто я еще не дорос до этого. Потому и спрашиваю, мешает ли что-то заюзать putStrLn в withNewIORef? Соответственно, если не мешает, то withNewIORef будет таким же вводом-выводом, как и экшн от putStrLn.
VE>Ок, пойдём дальше. Вот пишите вы себе на C++ мемоизатор. И что, поведение программы меняется? Ну т.е. вроде бы что-то меняется, как если одну сортировку на другую поменять, но вот поведение — не меняется. Так как время — не заложено в поведении.
Это зависит от прочих вещей. Если в мемоизированном коде был ввод-вывод, то поведение поменяется.
VE>>>Мои определения ничего такого не говорят. S>>Мне показалось что они указывают на то что выделение памяти есть семантически обозримый побочный эффект.
VE>А также на то, что является ли это поведением или просто implementation defined (т.е. программе в целом насрать на это), так сказать, определяет программист.
Если определяет программист, то это значит что не определяет никто другой. Соответственно, любые изменения порядка/кэширования и т.п. определяются программистом и никем другим.
VE>>>И какую такую левую функцию портит withTemporaryFile, которой не хватило прав? S>>я ничего не утверждал о порче функций. Я написал что выделять память чистой функции не запрещено.
VE>Не запрещено Абсолютом.
VE>Вот эту фразу поясните: VE>
VE>Дык это нормально. Чистой функции никто не запрещает жрать память. Портить левую — нельзя. А жрать — сколько влезет.
VE>Кто чего портит у withTemporaryFile?
putStrLn портит. Мне ведь ничего не мешает заюзать его с withTemporaryFile? Если что-то мешает, то там посмотрим.
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
VE>По поводу Haskell вы мне лучше скажите по вашим определениям, почему interact принимает чистую функцию, а newIORef возвращает IO (IORef a)?
Я не понимаю сути вопроса. Почему бы interact-у не принимать чистую функцию? И причем тут определения? И почему бы newIORef-у не возвращать IO?
Чему тут конкретно должны мешать определения, которые не мои?
Здравствуйте, samius, Вы писали:
VE>>И не должна. RT позволяет заменить вычисление значением без смены поведения программы. Поведение программы определяет программист. S>"(in other words, yielding a program that has the same effects and output on the same input)"
Именно. Поведение архиватора, например, какое? Файлы архивировать или память выделять? Вот первое — поведение, второе так, деталь реализации.
VE>>Какой такой грязной map? S>Тот самый map, который возвращает результат, выделяя память под него. Ты ведь предложил считать выделение памяти обозримым побочным эффектом, вот потому map стал грязным в тот же час.
Я сказал, что оно ничем не отличается от создания временного файла, и потому чисты они или нет (а точнее RT), зависит от ситуации, а не от самого факта. И map — RT.
VE>>
VE>>withTemporaryFile :: (Handle -> IO r) -> r
VE>>
S>Как я понимаю, такая сигнатура не мешает нагадить не только в этот файл, а вообще везде где взбредет.
Конкретно такая да. Если это принципиально, её можно переписать по аналогии с монадой ST и получить гарантии работы с одним файлом.
Суть сказанного от этого не меняется.
VE>>Если вы используете её так же, как runST, она вполне себе RT. Если вам необходимо именно создание временного файла, то необходим аналог stToIO. VE>>Что такое runST знаете? S>Еще нет. Но во всяком случае, об этом есть что почитать. А по поводу withTemporaryFile гугл молчит.
Тогда почитайте, потому что я на это больше всего упор делаю.
Вкратце: у нас есть монада ST, внутри которой (и только внутри) мы можем выделять память, работать с мутабельными переменными.
Однако так как от одних только махинаций с памятью мы гарантированно не сможем получить разные результаты, всю совокупность вычислений мы можем запихнуть в runST и получить чистый результат.
Т.е. почти как unsafePerformIO, но уже не unsafe, потому что у нас подмножество операций, не позволяющих выдать различные результаты.
S>Нет, не это спрашиваю. По этому поводу у меня свое мнение и пока никто меня не разубедил. Я спрашиваю о том, считаешь ли ты что все кто пишет что хаскель чист, пишут чушь?
Нет.
VE>>Если я пишу программу, которая именно что должна греть процессор, то да, вычисление списка не будет RT. Если я пишу калькулятор, то греет он процессор, открывает ли временные файлы, подключается ли к базе — это всё не важно. Если поведение от замены функций на значение (калькуляторное) не меняется, функции — RT. S>Вижу, что твоя RT тоже устроена по понятиям. Файлы и БД не считаются единым целым с программой. Чтение из них — это ввод. Если то, что ты прочитаешь из файла/БД будет влиять на результат вычисления, то его нельзя классифицировать как RT.
readIORef тоже тогда можно считать вводом. С т.з. языка ничем не отличается.
Касаемо чтения файла — не влиять, а менять результат от вызова к вызову, тогда не RT.
VE>>withNewIORef (withNewIORef :: a -> (IORef a -> IO b) -> b — не ввод-вывод). S>Нет? А что мне помешает подсунуть в withNewIORef экшн, возвращаемый putStrLn (который ввод-вывод)? Просто я еще не дорос до этого. Потому и спрашиваю, мешает ли что-то заюзать putStrLn в withNewIORef? Соответственно, если не мешает, то withNewIORef будет таким же вводом-выводом, как и экшн от putStrLn.
Посмотри runST, я в общем и целом про него, но на примере IO, в котором, разумеется, гарантию не дашь.
VE>>Ок, пойдём дальше. Вот пишите вы себе на C++ мемоизатор. И что, поведение программы меняется? Ну т.е. вроде бы что-то меняется, как если одну сортировку на другую поменять, но вот поведение — не меняется. Так как время — не заложено в поведении. S>Это зависит от прочих вещей. Если в мемоизированном коде был ввод-вывод, то поведение поменяется.
Открыли файл для чтения в начале программы. Потом пользуемся функцией readFileContents, возвращающую из раза в раз одно и то же по понятным причинам.
Поменяется ли поведение, если в этой функции прочесть один раз во внутренний буфер?
VE>>А также на то, что является ли это поведением или просто implementation defined (т.е. программе в целом насрать на это), так сказать, определяет программист. S>Если определяет программист, то это значит что не определяет никто другой. Соответственно, любые изменения порядка/кэширования и т.п. определяются программистом и никем другим.
Пусть. Зато уже не мешает такую функцию передать в map, например, требующую RT функцию.
VE>>Кто чего портит у withTemporaryFile? S>putStrLn портит. Мне ведь ничего не мешает заюзать его с withTemporaryFile? Если что-то мешает, то там посмотрим.
Да, забыл упомянуть про это, хотя держал в голове. Имел в виду подмножество IO (статически-гарантируемое), работающее только с этим файлом.
Само собой, с моей терминологией сделать RT getLine (точнее World -> (String, World)) не получится, но разговор о таком RT вообще бессмысленен. Можно, конечно, считать, что в эту функцию условно передаётся вся Вселенная (в общем-то, именно это и подразумевается), но зачем?
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
S>>"(in other words, yielding a program that has the same effects and output on the same input)"
VE>Именно. Поведение архиватора, например, какое? Файлы архивировать или память выделять? Вот первое — поведение, второе так, деталь реализации.
Согласен, но с натяжкой. Вот такие несущественные детали реализации, как логирование, или недетерминированность времени создания файла-архива, это тоже эффекты. И это такие эффекты, которые могут быть приняты во внимание при оценке чистоты архиватора. Если же рассматривать архиватор как функцию, принимающую параметры и байты файла и выдающую байты архива, то можно говорить о чистоте такой функции. Можем проводить параллели, а вообще сама программа архиватора и функция, которая архивирует в этом отношении будут разными вещами в разных контекстах.
S>>Тот самый map, который возвращает результат, выделяя память под него. Ты ведь предложил считать выделение памяти обозримым побочным эффектом, вот потому map стал грязным в тот же час.
VE>Я сказал, что оно ничем не отличается от создания временного файла, и потому чисты они или нет (а точнее RT), зависит от ситуации, а не от самого факта. И map — RT.
Со оговоркой по поводу того что файл "гарантирует" общение лишь с ним и что чтение будет лишь того что записано, без всяких "наводок", то — да, соглашусь, что на это можно смотреть одинаково (на такой временный файл и работу с памятью).
S>>Как я понимаю, такая сигнатура не мешает нагадить не только в этот файл, а вообще везде где взбредет.
VE>Конкретно такая да. Если это принципиально, её можно переписать по аналогии с монадой ST и получить гарантии работы с одним файлом.
Принципиально, но строить ничего не надо, достаточно принять такую гарантию. VE>Суть сказанного от этого не меняется.
Я понял, о чем ты говорил, просто до конца пытался показать что такой трюк позволяет втиснуть в него взаимодействие с другим миром, где эффекты для нас значимы.
S>>Еще нет. Но во всяком случае, об этом есть что почитать. А по поводу withTemporaryFile гугл молчит.
VE>Тогда почитайте, потому что я на это больше всего упор делаю.
Обязательно в ближайшее время. Инфы с документации мне явно не хватило, что бы перехватить смысл, попытаюсь вкурить Lazy Functional State Threads, упомянутый в статье.
VE>Вкратце: у нас есть монада ST, внутри которой (и только внутри) мы можем выделять память, работать с мутабельными переменными. VE>Однако так как от одних только махинаций с памятью мы гарантированно не сможем получить разные результаты, всю совокупность вычислений мы можем запихнуть в runST и получить чистый результат.
Это как если мы в C++ напишем чистую функцию, которая будет дергать нечистые с локальными (относительно внешней функции) побочными эффектами, но сама внешняя при этом может быть формально чиста. Что-то похожее?
VE>Т.е. почти как unsafePerformIO, но уже не unsafe, потому что у нас подмножество операций, не позволяющих выдать различные результаты.
Интересно, попробую найти ответы в Lazy Functional State Threads.
S>>Нет, не это спрашиваю. По этому поводу у меня свое мнение и пока никто меня не разубедил. Я спрашиваю о том, считаешь ли ты что все кто пишет что хаскель чист, пишут чушь? VE>Нет.
Рад. Тогда может быть это намек на то, что ты и эти авторы по-разному воспринимают чистоту?
S>>Вижу, что твоя RT тоже устроена по понятиям. Файлы и БД не считаются единым целым с программой. Чтение из них — это ввод. Если то, что ты прочитаешь из файла/БД будет влиять на результат вычисления, то его нельзя классифицировать как RT.
VE>readIORef тоже тогда можно считать вводом. С т.з. языка ничем не отличается. VE>Касаемо чтения файла — не влиять, а менять результат от вызова к вызову, тогда не RT.
Что-то в этом есть. Т.е. если ты как разработчик даешь зуб что из БД будет прочитано одно и то же при любом стечении обстоятельств, при котором вообще разумно ожидать результатов выполнения программы, тогда, может быть, имеет смысл говорить об RT. Но таки с оговоркой, что такое RT не может быть выкуплено компилятором с умыслом произвести оптимизацию.
VE>Посмотри runST, я в общем и целом про него, но на примере IO, в котором, разумеется, гарантию не дашь.
Ок.
S>>Это зависит от прочих вещей. Если в мемоизированном коде был ввод-вывод, то поведение поменяется.
VE>Открыли файл для чтения в начале программы. Потом пользуемся функцией readFileContents, возвращающую из раза в раз одно и то же по понятным причинам. VE>Поменяется ли поведение, если в этой функции прочесть один раз во внутренний буфер?
Нет.
S>>Если определяет программист, то это значит что не определяет никто другой. Соответственно, любые изменения порядка/кэширования и т.п. определяются программистом и никем другим.
VE>Пусть. Зато уже не мешает такую функцию передать в map, например, требующую RT функцию.
Не мешает.
S>>putStrLn портит. Мне ведь ничего не мешает заюзать его с withTemporaryFile? Если что-то мешает, то там посмотрим.
VE>Да, забыл упомянуть про это, хотя держал в голове. Имел в виду подмножество IO (статически-гарантируемое), работающее только с этим файлом.
Да, я это предпологал.
VE>Само собой, с моей терминологией сделать RT getLine (точнее World -> (String, World)) не получится, но разговор о таком RT вообще бессмысленен. Можно, конечно, считать, что в эту функцию условно передаётся вся Вселенная (в общем-то, именно это и подразумевается), но зачем?
Вот и я об этом же. Зачем? Особенно, зачем такое считать, если нам не нужна формальная чистота IO String? Мы можем смело называть IO String грязной, не RT, и ничего нам от этого не будет. Хаскель не станет хуже.
Что же касается локальных миров, то у меня мнение следующее: Я готов считать формально чистой функцию, если гарантируется, что все ее взаимодействие с неким локальным миром а) детерминировано, б) не имеет влияние на глобальный мир (тут с кучей оговорок). Т.е. если ты докажешь, что твоя функция (и только она) пишет в файл результаты детерминированных вычислений, читает их детерминированным образом (что само по себе звучит кощунственно по отношению к чтению файла), исключая изменения данных файла извне, если внешний мир (по отношению к этому файлу) не узнает о существовании этого файла, либо даже просто будет делать вид что его нет и не было. Соответственно, вместо файла могут выступать голуби, Софокл с пергаментом (пергамент придется съесть) и т.п.
Но это будет такая, "пользовательская чистота", или чистота в неком контексте, отличающегося от того, что по умолчанию. И все-таки, такая чистота нужна лишь для того, что бы утверждать что совокупность грязных действий имеет локальный и/или незначительный эффект на результат вычислений.
Здравствуйте, MigMit, Вы писали:
MM>А, в этом смысле. Понял. Но тут, скорее, играет "двустороннесть" правил пролога, это не относится именно к ПМ.
Относится. Такое же точно программирование в ограничениях, только полноценное. ИМХО, большую часть работы для статически-типизированного языка можно было бы делать в compile-time, и только если данных недостаточно — вызывать поиск в рантайм.
Здравствуйте, Klapaucius, Вы писали:
K>Нет. MigMit раньше исходил из ошибочного предположения, что в C++ есть параметрический полиморфизм. Но параметрический полиморфизм означает, что forall a. Box a описывает бесконечное кол-во типов.
Почему? ad-hoc — это же не соседний класс, а подкласс чистого полиморфизма, частный случай.
Можно объявить "бесконечное" мн-в типов в С++:
K>В C++ же кол-во типов всегда конечно, поэтому полиморфизм там только ad-hoc.
Обычно для показанной техники делают adhoc только для BigN=0, но и это необязательно. Точнее сформулируем так: можно использовать только те типы, которые выводимы во время компиляции, т.е. независимы от данных. И тогда мн-во реально задействованных в программе типов ес-но будет конечным, но это могут быть любые типы из почти бесконечного объвленного мн-ва:
Здравствуйте, Klapaucius, Вы писали:
T>>Я не поленился и перепёр haskell в шаблоны один в один: K>Ох. Лучше бы вы не поленились читать то, что писал МигМит в том треде и что сейчас пишу я. K>Вот смотрите:
K>Видите, этот код принимает число как аргумент командной строки. Он компилируется. НЕ интерпретируется.
Неправда, он интерпретируется в этом месте:
ScalarProduct a => ScalarProduct (Cons a) where
В отличие от Haskel, подобное преобразование типов (распаковка) для кода Tonal- выполняется в compile-time.
K>И типы проверяет статически.
Тоже неправда. Для АлгТД статически проверяется только м-но допустимых запакованных типов, то бишь статически проверяется лишь некое ограничение на возможный тип, но не сам запакованный тип. Конкретный тип распаковывается исключительно в рантайм, а сама техника распаковки тождественна технике dynamic_cast (с точностью до деталей, то бишь с точностью до способа кодирования/представления токена типа для целей рантайма).
K>Закомментированная строчка вызовет ошибку. Потому, что проверяются не абсолютные размеры списков, а то, что списки одинакового размера. А это известно на этапе компиляции, завит только от написанного кода, а не входных данных.
И опять неправда и непонимание происходящего. Это зависит от реализованной системы типов. Там, где ML-яык работает с боксированными значениями в рантайм, там мы имеем классическую эмуляцию динамики/интерпретации, хоть и со статическими проверками допустимости мн-ва АлгТД в момент компиляции. А там, где ML-язык будет пытаться инстанциировать типы целиком, чтобы избежать лишней динамики в рантайм, без техники зависимых типов не обойтись.
K>И зависимые типы для этого не нужны
Они не нужны только для боксированной реализации рекурсивных типов и прочей динамической типизации.
Здравствуйте, samius, Вы писали:
K>>Я тут уже указывал способ определить не верхнюю границу, а точное число типов для тизируемого кода на C++ S>Остается выяснить, что же мешает превзойти заранее заданное точное число типов.
Мешает вид полиморфизма в C++
S>Но мы обсуждаем ПП, фичу языка, а не конкретную программу. Причем тут конкретная программа — непонятно.
При том, что фича языка позволяет писать определенный класс конкретных программ. А ее отсутствие — не позволяет.
Рассуждать про n программ, которые в сумме что-то реализуют — это софистика. Ну, как говорить, что (int,+,*) это не кольцо вычетов, а кольцо целых чисел, складывая модули кольца вычетов во всех возможных программах.
K>>И потенциальная бесконечность следует для одной программы. S>Нет.
Да.
K>>Из определения парам. полиморфизма следует, что у нас есть, по крайней мере, один мономорфный тип () и один полиморфный forall a. Succ a с этим вы согласны? S>Нет. Из определения ПП следует лишь то, что такие типы полиморфным кодом должны обрабатываться единым образом.
Вы берете удобную для себя половину определения, которое я тут
привел полностью. Одна только однородность кода не является каким-то характеристическим признаком параметрического полиморфизма. Сабтайпинг, например, тоже позволяет писать однородный код. Собственно, компилятор Java и переписывает однородный код на языке с параметрическим полиморфизмом в однородный код с использованием сабтайпинга на языке, в котором ПП нет вовсе.
S> А то что они должны существовать, да еще и в какой-то конкретной программе, да еще и потенциальной бесконечностью — увы, не следует.
А неудобную для вас вторую часть определения, о том, как однородный код типизируется вовсе отбрасываете. Из второй части определения следует, что они должны существовать.
В C++ они, разумеется, не существуют. Шаблонный код на C++ вообще не может быть типизирован (отсюда и вся мощность шаблонов) и не типизируется. В некоторых реализациях вроде gcc проверяются кайнды (* или Nat), в некоторых, вроде, макрософтовской — даже кайнды не проверяются (ну или не проверялись раньше). Типизируется только инстанциация, результат применения. В языках с ПП типизируется и однородный код и само применение.
S>что мешает сконструитьвать еще один тип в программе на C++? Разумеется, явно в коде, или в компайл-тайме.
Отсутствие в системе типов C++ типа forall a. Succ a.
S>Если обсуждаются примеры из http://migmit.livejournal.com/32688.html, то С# там их применяет в рантайме.
Покажите применение в рантайме из того кода.
S>Тогда вопрос. Откуда C# в компайл тайм знает что надо применить как минимум 1000 типов? Для 1000 код работает, мог бы работать и для большего числа, если бы не StackOverflow.
Еще раз, параметрический код не требует для проверки знания числа типов. Ну, как типизация функции + над int не требует построения и типизации таблицы констант для всего декартова произведения двух множеств значений int.
S>
S>#
S>*
S>(?,...)->? -- unbounded type name
S>
Ну да, это я и имел в виду.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, vdimas, Вы писали:
V>Почему? ad-hoc — это же не соседний класс, а подкласс чистого полиморфизма, частный случай.
Нет. Ad-hoc это соседний класс. Никакого "чистого полиморфизма" не бывает. Бывают такие виды порлиморфизма: параметрический полиморфизм (дженерики), ad-hoc (перегрузка/специализация) и сабтайпинг (полиморфизм через наследование) + возможные сочетания.
V>Можно объявить "бесконечное" мн-в типов в С++: V>
Ну и где тут (потенциальная) бесконечность типов? Верхняя граница тут явно задоается.
K>>В C++ же кол-во типов всегда конечно, поэтому полиморфизм там только ad-hoc.
V>Обычно для показанной техники делают adhoc только для BigN=0, но и это необязательно. Точнее сформулируем так: можно использовать только те типы, которые выводимы во время компиляции, т.е. независимы от данных. И тогда мн-во реально задействованных в программе типов ес-но будет конечным, но это могут быть любые типы из почти бесконечного объвленного мн-ва:
Ну, в обсуждаемом примере все типы выводимы во время компиляции (именно это под словом "типы" обычно и понимают). От данных они тоже независисмы. Но шаблоны таким способом использовать нельзя.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll