Здравствуйте, samius, Вы писали:
S>В определениях не пишут "почему". В определениях пишут о том, что является определяемым понятием, а что — нет. Функции с вводом выводом чистыми не являются по определению.
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
S>>В определениях не пишут "почему". В определениях пишут о том, что является определяемым понятием, а что — нет. Функции с вводом выводом чистыми не являются по определению.
VE>Каков критерий ввода-вывода?
http://en.wikipedia.org/wiki/Input/output
Добавлю только (от себя), что работу всякого рода механизмов, обеспечивающие работу "памяти" прозрачно для самой программы (вроде виртуальной памяти), я не рассматриваю как ввод вывод в отношении выполняемой программы. Наверное, следует отнести их к вводу/выводу операционной системы.
Здравствуйте, Klapaucius, Вы писали: T>>В С++ количество описываемых шаблонами типов бесконечно. K>Это совсем не очевидно, скорее очевидно обратное. Число типов, описываемых шаблоном — это число инстанциаций шаблона с разными типами. Т.е. понятно, что с одинаковым кодом можно как-то бороться, но семантика именно такая — для каждого подстаялемого в шаблон типа — своя реализация. Т.е. это ad-hoc полиморфизм, а не параметрический.
Ежели число описываемых типов не бесконечно, значит оно ограничено для любых условий и можно привести оценку сверху этого ограничения (M мало, возмём N).
Приведи хотя бы для простейшего случая:
С другой стороны ты прав в том, что число воплощений (инстанций) шаблона в конкретной программе всегда ограничено.
Но это и понятно, ведь шаблоны вводились в С++ не как универсальный язык общего назначения, а как средство генерирования кода.
T>>Но бесконечно только на этапе компиляции — именно в это время идут вычисления над типами. T>>На этапе же выполнения остаётся только код скомпилированный для конечного числа типов. Либо компиляция не завершается. K>Рантайм-то тут вообще причем? Обсуждаемые примеры работают на этапе компиляции, что на хаскеле — что на C#. Динамические возможности CLR используются в C# для генерации специализаций для анбоксед-типов (т.е. для оптимизации, на семантику это не влияет). В хаскеле и яве полиморфизм бывает только для боксед, но и тут есть некоторые обходные пути для оптимизации. Понятно, что C++ не может позволить себе полиморфизм ни ценой боксинга всего, ни ценой JIT. Потому-то там параметрического полиморфизма и нет.
C++ Не может позволить себе динамический ПП. Статический, времени компиляции, ПП в нём есть. T>>См. мой комментарий
K>Это вообще нерелевантно обсуждаемой теме. Мы говорим о случаях, когда все что нужно известно на этапе компиляции.
Вроде обсуждали вот этот пост.
Я его упростил.
И в коде уважаемого MigMit, и в моём с помощью шаблонов описывается бесконечное количество типов.
Но конкретный тип (множество типов в примере MigMit) для воплощения на этапе компиляции не задаётся, на чём компилятор С++ и обламывается.
В случае Java/.net это решается тем, что у них есть возможность компиляции в рантайме. Это не JIT это ближе к интерпретации.
Причём, я не помню есть ли в С++ требования непременного воплощения всех типов используемых для воплощения указанного — вроде нет.
Так что вполне возможны компиляторы, которые не будут создавать временные воплощения используемые только во время вычислений на типах.
Но и это не сделает работоспособными обсуждаемые примеры — ведь конкретное воплощение таки задаётся в рантайме...
Здравствуйте, samius, Вы писали:
S>Функции с вводом выводом чистыми не являются по определению.
Если под общепринятым определением понимается википедийное, то там ведь явным образом оговаривается, что сайд-эффект должен быть "semantically observable". Нет?
... << 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
Здравствуйте, Tonal-, Вы писали:
T>Ежели число описываемых типов не бесконечно, значит оно ограничено для любых условий и можно привести оценку сверху этого ограничения (M мало, возмём N). T>Приведи хотя бы для простейшего случая: T>
Это неполный код, который вообще не имеет смысла и не может быть скомпилирован (иначе, чем просто выкинут) без инстанциаций. Если вы приведете полный код с инстанциациями для N типов, то верхняя оценка будет N. В языке с ПП похожий по виду (но не по смыслу) код полностью самодостаточен и может быть скомпилирован.
T>С другой стороны ты прав в том, что число воплощений (инстанций) шаблона в конкретной программе всегда ограничено.
То-то и оно.
T>Но это и понятно, ведь шаблоны вводились в С++ не как универсальный язык общего назначения, а как средство генерирования кода.
И снова верно. Но причем тут ПП?
T>C++ Не может позволить себе динамический ПП.
"Динамического" ПП не существует.
T> Статический, времени компиляции, ПП в нём есть.
Нет.
T>Вроде обсуждали вот этот пост. T>Я его упростил.
Вы написали код, требующий зависимых типов. МигМит написал код, для которого достаточно параметрического полиморфизма в стиле ML (и с ограниченной квантификацией). Следовательно, об упрощении говорить не приходится.
T>В случае Java/.net это решается тем, что у них есть возможность компиляции в рантайме. Это не JIT это ближе к интерпретации.
Вы, похоже, недостаточно внимательно прочли мой пост, на который отвечаете. Компиляция в рантайме в C# решает в данном случае только часть проблем с производительностью, она для оптимизации используется. И если мне беспамятство не изменяет, в первых бетах CLR 2 такой оптимизации не было. В Java рантайм-компиляция тут вообще ничего не решает: в рантайме компилируется вовсе не Ява, а язык, в котором параметрического полиморфизма нет. В случае с хаскелем, ни сам он, ни какой из промежуточных языков в рантайме не компилируется. Никакая компиляция в рантайме для ПП вообще не обязательна.
T>конкретное воплощение таки задаётся в рантайме...
Нет, конкретное воплощение задается не в рантайме, а в компайлтайме.
Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, samius, Вы писали:
S>>Функции с вводом выводом чистыми не являются по определению.
K>Если под общепринятым определением понимается википедийное, то там ведь явным образом оговаривается, что сайд-эффект должен быть "semantically observable". Нет?
Чего гадать то?
Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
S>Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
Ну, о чем я и говорил.
... << 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>>
S>>Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
K>Ну, о чем я и говорил.
А это о чем я говорил. (подчеркнул)
S>>>Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.
S>А это о чем я говорил. (подчеркнул)
Очевидно, что "semantically observable" относится и к side effect и к output. Иначе такое определение просто не имеет смысла.
... << 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, Вы писали:
K>Очевидно, что "semantically observable" относится и к side effect и к output. Иначе такое определение просто не имеет смысла.
Я не понимаю, что такое semantically observable output. Пояснишь?
Здравствуйте, samius, Вы писали:
K>>Очевидно, что "semantically observable" относится и к side effect и к output. Иначе такое определение просто не имеет смысла. S>Я не понимаю, что такое semantically observable output. Пояснишь?
Странно, тут рядом ваше сообщение, из которого можно сделать вывод, что понимаете. Т.е. функция форсирующая список, не помещающийся в память выполняет "семантически ненаблюдаемый" I/O. Но с помощью трюков с уникальным значением RealWorld как раз и достигается семантическая ненаблюдаемость (невозможность нарушить ссылочную прозрачность) для любого I/O. А иначе — за что боролись? — если I/O производит семантически наблюдаемые сайд-эффекты, много ли смыла ограничивать возможность создать "семантически наблюдаемый" сайд-эффект без I/O? У GHC даже прайм-опов нарушающих ссылочную прозрачность нет, нарушить может только их "неудачная" комбинация, что и решается абстрактным типом IO для которого все комбинаторы дают только "удачные" комбинации.
... << 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, Вы писали:
K>>>Очевидно, что "semantically observable" относится и к side effect и к output. Иначе такое определение просто не имеет смысла. S>>Я не понимаю, что такое semantically observable output. Пояснишь?
K>Странно, тут рядом ваше сообщение, из которого можно сделать вывод, что понимаете. Т.е. функция форсирующая список, не помещающийся в память выполняет "семантически ненаблюдаемый" I/O.
Я написал, что вообще такое за I/O не считаю. Не программа себя сбрасывает в свап.
K>Но с помощью трюков с уникальным значением RealWorld как раз и достигается семантическая ненаблюдаемость (невозможность нарушить ссылочную прозрачность) для любого I/O.
Как это? Т.е. мы заменяем акцию с выводом в файл ее результатом и получаем на диске файл?
K> А иначе — за что боролись? — если I/O производит семантически наблюдаемые сайд-эффекты, много ли смыла ограничивать возможность создать "семантически наблюдаемый" сайд-эффект без I/O?
I/O производит I/O. Я не понимаю, как можно сематнически наблюдать отправленные байты по сетевому протоколу.
K>У GHC даже прайм-опов нарушающих ссылочную прозрачность нет, нарушить может только их "неудачная" комбинация, что и решается абстрактным типом IO для которого все комбинаторы дают только "удачные" комбинации.
Дык речь не о чистоте комбинирования IO, а о чистоте выполнения акций.
Здравствуйте, samius, Вы писали:
S>Как это? Т.е. мы заменяем акцию с выводом в файл ее результатом и получаем на диске файл?
Мы берем мир без файла на диске и возвращаем мир с файлом на диске.
S>I/O производит I/O. Я не понимаю, как можно сематнически наблюдать отправленные байты по сетевому протоколу.
Если мы можем сравнить мир в котором байты идут по протоколу и мир в котором не идут, мы сможем произвести семантическое наблюдение. Но мы не можем.
S>Дык речь не о чистоте комбинирования IO, а о чистоте выполнения акций.
Речь тут все время о чистоте выполнения акций. Комбинирование — оно любое будет чистым. А вот скомбинированная акция может и не быть, если уникальность RealWorld нарушается. Потому оставляются только такие комбинаторы, результат которых будет чистым.
... << 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, Вы писали:
VE>>Каков критерий ввода-вывода?
S>http://en.wikipedia.org/wiki/Input/output S>Добавлю только (от себя), что работу всякого рода механизмов, обеспечивающие работу "памяти" прозрачно для самой программы (вроде виртуальной памяти), я не рассматриваю как ввод вывод в отношении выполняемой программы. Наверное, следует отнести их к вводу/выводу операционной системы.
А хард — это ввод-вывод? А USB-вентилятор? А вентилятор внутри системного блока? А нагревательный USB-прибор? А нагревательный прибор "процессор" внутри системного блока?
Какое-то интуитивное понятие, не формализованное.
Я бы вообще не использовал понятие "чистота", а только ссылочную прозрачность. Есть у нас гарантия, что factorial вернёт одно и то же, так пусть хоть через голубиную почту получает.
Здравствуйте, Klapaucius, Вы писали: T>>Ежели число описываемых типов не бесконечно, значит оно ограничено для любых условий и можно привести оценку сверху этого ограничения (M мало, возмём N). T>>Приведи хотя бы для простейшего случая: T>>
K>Это неполный код, который вообще не имеет смысла и не может быть скомпилирован (иначе, чем просто выкинут) без инстанциаций. Если вы приведете полный код с инстанциациями для N типов, то верхняя оценка будет N. В языке с ПП похожий по виду (но не по смыслу) код полностью самодостаточен и может быть скомпилирован.
Ещё раз.
В С++ вычисления проводятся в 2е стадии.
1. Во время компиляции проводятся вычисления констант и вычисления на типах: разрешения совмещений (перегрузки), определения конкретных типов шаблонов которые требуют воплощения.
Всё это делается для построения конкретного кода.
2. Во время выполнения работает построенный на первом этапе код.
Вычисления в первой фазе — иммутабельные, чисто функциональные. Именно здесь работает статический полиморфизм. Причём совмещения — это Ad hos, а шаблоны ПП.
В фазе выполнения почти никакая информация о типах не сохраняется. Работает динамический полиморфизм (виртуальность) через таблицы методов.
Т. е. приведённый мной код действительно не имеет смысла в рантайме но вполне может быть востребован во время компиляции.
Впрочем как и большинство кода stl.
T>>Вроде обсуждали вот этот пост. T>>Я его упростил. K>Вы написали код, требующий зависимых типов. МигМит написал код, для которого достаточно параметрического полиморфизма в стиле ML (и с ограниченной квантификацией). Следовательно, об упрощении говорить не приходится.
Я выделил ту часть, которая не даёт этим примерам скомпилироватся в С++.
Вне зависимости от типа полиморфизма эта часть общая.
T>>конкретное воплощение таки задаётся в рантайме... K>Нет, конкретное воплощение задается не в рантайме, а в компайлтайме.
Значит у нас разные понятия что такое воплощение.
У меня С++ное, а у тебя?
K>Вообще, эта ветка уже начинает напоминать уморительно смешной тред МигМита на ухогорлоносе.
О, спасибо за ссыль.
Я не поленился и перепёр haskell в шаблоны один в один:
//data Nil = Nilstruct Nil {};
//data Cons a = Cons Integer atemplate <int N, class A> struct Cons {
static const int val = N;
typedef A Rest;
};
//class ScalarProduct a where
// scalarProduct :: a -> a -> Integertemplate <class T1, class T2> struct scalarProd;
//instance ScalarProduct Nil where
// scalarProduct Nil Nil = 0template <> struct scalarProd<Nil, Nil> {
static const int res = 0;
};
//instance ScalarProduct a => ScalarProduct (Cons a) where
// scalarProduct (Cons n1 a1) (Cons n2 a2) = n1 * n2 + scalarProduct a1 a2template <int N1, int N2, class A1, class A2>
struct scalarProd<Cons<N1, A1>, Cons<N2, A2> > {
static const int res = N1 * N2 + scalarProd<A1, A2>::res;
};
//main' :: ScalarProduct a => Integer -> Integer -> a -> a -> Integertemplate <int N, int I, class AS, class BS> struct make_quot;
//main' 0 _ as bs = scalarProduct as bstemplate <int I, class AS, class BS> struct make_quot<0, I, AS, BS> {
static const int res = scalarProd<AS, BS>::res;
};
//main' n i as bs = main' (n-1) (i+1) (Cons (2*i+1) as) (Cons (i^2) bs)template <int N, int I, class AS, class BS> struct make_quot {
static const int res = make_quot<N - 1, I + 1, Cons<2*I + 1, AS>, Cons<I*I, BS> >::res;
};
//main :: Integer -> Integer
//main n = main' n 0 Nil Nil wheretemplate <int N> struct make {
static const int res = make_quot<N, 0, Nil, Nil>::res;
};
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, samius, Вы писали:
VE>>>Каков критерий ввода-вывода?
S>>http://en.wikipedia.org/wiki/Input/output
VE>А хард — это ввод-вывод? А USB-вентилятор? А вентилятор внутри системного блока? А нагревательный USB-прибор? А нагревательный прибор "процессор" внутри системного блока?
Если программа посылает данные USB вентилятору, то это определенно вывод. Если принимает — определенно ввод. С другими девайсами аналогично.
VE>Какое-то интуитивное понятие, не формализованное.
Да, и что?
VE>Я бы вообще не использовал понятие "чистота", а только ссылочную прозрачность. Есть у нас гарантия, что factorial вернёт одно и то же, так пусть хоть через голубиную почту получает.
Если факториал считать голубинной почтой, то это ввод, вывод, + зависимость от голубей, коршунов, погоды, и прочей хрени. Детерминированным такой результат нельзя назвать даже интуитивно.
Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, samius, Вы писали:
S>>Как это? Т.е. мы заменяем акцию с выводом в файл ее результатом и получаем на диске файл?
K>Мы берем мир без файла на диске и возвращаем мир с файлом на диске.
Что за чудеса? Мир — внутренний объект программы, файл — внешний.
этак можно сказать что fprintf принимает неявно внешний мир и возвращает длину строки и новый мир с файлом.
S>>I/O производит I/O. Я не понимаю, как можно сематнически наблюдать отправленные байты по сетевому протоколу.
K>Если мы можем сравнить мир в котором байты идут по протоколу и мир в котором не идут, мы сможем произвести семантическое наблюдение. Но мы не можем.
S>>Дык речь не о чистоте комбинирования IO, а о чистоте выполнения акций.
K>Речь тут все время о чистоте выполнения акций. Комбинирование — оно любое будет чистым. А вот скомбинированная акция может и не быть, если уникальность RealWorld нарушается. Потому оставляются только такие комбинаторы, результат которых будет чистым.
Речь о том, что акция может не быть чистой не потому что уникальность мира, а потому что она делает ввод/вывод.
Здравствуйте, Tonal-, Вы писали:
T>В фазе выполнения почти никакая информация о типах не сохраняется.
Еще раз. Мы не говорим о времени выполнения. Вообще. Только о времени компиляции. О компиляции говорим, а о выполнении — наоборот — не говорим. Речь идет о компиляции. О компиляции речь идет. О компиляции. Понятно?
T>Т. е. приведённый мной код действительно не имеет смысла в рантайме но вполне может быть востребован во время компиляции.
Вот в том и дело, что может быть "востребован". А скомпилировать без какой-то конкретной специализации его нельзя. Можно только прекомпилировать — т.е. распарсить и сериализовать AST, например. Или скомпилировать в код, который порождает исходный из другого исходного. А просто скомпилировать, в код, который просто работает, его нельзя. А вот
id x = x
скомпилировать — наоборот — можно. И он будет лежать в скомпилированном модуле и ждать своего часа. Потому, что параметрический полиморфизм — это когда код один для любого типа (или для множества типов (бесконечного), соотествующих определенному предикату). Любого числа типов. Написанных до этого кода и после него. Но в c++ параметрического полиморфизма нет — там не бывает кода для любого типа — только для конечного набора предзаданных конкретных — т.е. ad-hoc.
T>Впрочем как и большинство кода stl.
Вот именно.
T>Я выделил ту часть, которая не даёт этим примерам скомпилироватся в С++. T>Вне зависимости от типа полиморфизма эта часть общая.
Вы не выделяли общей части — у этих примеров нет ничего общего. Они решают разные задачи разными способами.
При этом вы последовательно игнорируете все мои замечания по существу. Про то, что в случае Java язык в котором есть ПП компилируется статически, а тот, что JIT-ится — ПП не имеет. И то, что GHC вовсе нет JIT — так что ни на какую "динамику" и "интерпретацию" тут не свалить.
T>Я не поленился и перепёр haskell в шаблоны один в один:
Ох. Лучше бы вы не поленились читать то, что писал МигМит в том треде и что сейчас пишу я.
Вот смотрите:
module Main where
import System.Environment
data Nil = Nil
data Cons a = Cons Integer a
class ScalarProduct a where scalarProduct :: a -> a -> Integer
instance ScalarProduct Nil where
scalarProduct Nil Nil = 0
instance ScalarProduct a => ScalarProduct (Cons a) where
scalarProduct (Cons n1 a1) (Cons n2 a2) = n1 * n2 + scalarProduct a1 a2
test :: Integer -> Integer
test n = test' n 0 Nil Nil where
test' :: ScalarProduct a => Integer -> Integer -> a -> a -> Integer
test' 0 _ as bs = scalarProduct as bs
test' n i as bs = test' (n-1) (i+1) (Cons (2*i+1) as) (Cons (i^2) bs)
-- test' n i as bs = test' (n-1) (i+1) as (Cons (i^2) bs)
main = print . test . read . head =<< getArgs
Видите, этот код принимает число как аргумент командной строки. Он компилируется. НЕ интерпретируется. И типы проверяет статически. Закомментированная строчка вызовет ошибку. Потому, что проверяются не абсолютные размеры списков, а то, что списки одинакового размера. А это известно на этапе компиляции, завит только от написанного кода, а не входных данных. И зависимые типы для этого не нужны — нужен параметрический полиморфизм в стиле ML + ограниченная квантификация (в Haskell — классы типов, в Java/C# констрейнты), которого в C++ нет.
... << 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>Потому, что параметрический полиморфизм — это когда код один для любого типа (или для множества типов (бесконечного), соотествующих определенному предикату). Любого числа типов. Написанных до этого кода и после него. Но в c++ параметрического полиморфизма нет — там не бывает кода для любого типа — только для конечного набора предзаданных конкретных — т.е. ad-hoc.
В определении полиморфизма нет ни слова о времени компиляции и вообще компиляции.
K>При этом вы последовательно игнорируете все мои замечания по существу. Про то, что в случае Java язык в котором есть ПП компилируется статически, а тот, что JIT-ится — ПП не имеет. И то, что GHC вовсе нет JIT — так что ни на какую "динамику" и "интерпретацию" тут не свалить.
Т.е. в C# нет ПП потому что джитится?
Здравствуйте, samius, Вы писали:
S>В определении полиморфизма нет ни слова о времени компиляции и вообще компиляции.
Это, конечно, верно. И в языке, на котором нет возможности написать код такого типа, как обсуждаемый здесь (какой-нибудь ML, с оговорками) параметрический полимoрфизм вполне можно реализовать c помощью кодогенерации и анализа всей программы (как в каком-нибудь MLton, с оговорками). Раздельную компиляцию мы потеряем, но иначе как в длительности компиляции это не проявится. Написать код, который вроде бы должен тайпчекаться, при условии что у нас есть ПП, но не тайпчекается мы не можем. А не пойман — не вор. Не будь в C++ сабтайпинга — обнаружить указанную проблему было бы невозможно. По крайней мере таким способом. Ну а проблемы с раздельной компиляцией без каких-то семантических эффектов на уровне языка — это просто детали реализации и говорить о проблемах с ПП повода не дают.
S>Т.е. в C# нет ПП потому что джитится?
Нет, почему же? В C# ПП есть, а то, что JIT генерирует специализации для value-типов — это деталь реализации. Это просто был контрдовод против утверждения Tonal- (довольно странного), что ПП может работать только при условии динамической компиляции специализаций или интерпретации. Это, разумеется, не верно.
... << 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