LINUX.ORG.RU

Глобальное состояние в ФП

 ,


2

3

А правильно ли я понимаю, что под «отсутствием состояния» в ФП понимается только отсутствие состояния переменных. У самого вычислителя, есть глобальное состояние, он в каждый момент времени находится в состоянии одной из редукций выражения, подобно тому, как машина Тьюринга в каждый момент вычисления «обозревает» строго определенную ячейку, на которой находится головка? Из этого следует, что ФП вычислитель относится к классу «Global State Machine» и у него, разумеется, ЕСТЬ глобальное состояние?

Ответ на: комментарий от emulek

#include <iostream>

struct Unit {};

template <typename T>
class IO;

using Void = IO<Unit>;

extern const Void done;

template <typename T>
class IO {
    T const c;
public:
    explicit IO(T const& c) : c(c) {}
    friend T const unsafeRun(IO<T> const& io) { return io.c; }
    friend Void print(IO<T> const& io) { std::cout << io.c << std::endl; return done; }
};

const Void done = Void(Unit());

template <typename T>
class Ref {
    T v;
    explicit Ref(T const& v) : v(v) {}
public:
    template <typename U>
    friend IO<Ref<U>> const make_ref(T const& v) { return IO<Ref<U>>(Ref<U>(v)); }
    friend Void const operator+=(Ref<T>& r, T const& x) { r.v += x; return done; }
    IO<T> const operator*() const { return IO<T>(v); }
};

Ref<int> counter = unsafeRun(make_ref<int>(0));

Void f(int x) {
    counter += x;
    return done;
}

IO<int> g() {
    return *counter;
}

int main() {
    f(2);
    f(3);
    auto x = g();
    print(x);
}
quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

ИМХО, глобальные переменные вообще не ломают ФП, по-сути. ФП ломают энергичные вычисления. Если мы определяем функцию, скажем, f=function(x)(+ x x), с точки зрения функции, при вызове, например, f(а), она возвратит (+ a a), а чем является этот а в контексте вызова, функцию не волнует. Она в любом случае возвращает всегда одно и то же. И даже если она содержит свободную муттабельную переменную, f=function(x)(+ a x), она возвращает (+ a x), не больше и не меньше. Что такое «a» в контексте вызова, ее не волнует. Вот таким должен был бы Ъ-взгляд на ФП, если бы не жили в параноидальном мире хомячья, которое почему то боится, что кто-то что-то изменит.

anonymous
()
Ответ на: комментарий от anonymous

Это всё очень неправильно :)

То есть это софистика уровня «любая подпрограмма принимает одну уникальную хрень (уникальное имя аргумента f)» и «возвращает одну и ту же уникальную хрень (выражение f)» => «все подпрограммы это чистые функции из {имена аргументов} -> {тело функции}» и можно расходиться. То есть тут мы вообще к рассмотрению вопроса не подошли (что есть вещи и отображения которые моделируются типами и программами языка).

Пусть есть какой-то A : Type, _+_ : A * A -> A, f (x : A) = (x + x) : A это f : A -> A, то есть для всех a : A выражение f a (: A) в конечном итоге можно заменить на e : A (refl : Eq A (f a) e), а вот f(x : A) = (a + x) : A при мутабельном a : A это не функция, функцией снова она будет если Ref : Type -> Type, IO : Type -> Type, _+_ : Ref A * A -> IO A, a : Ref A, f(x : A) = (a + x) : IO A, f : A -> IO A, тогда для любого a : A выражение f a : IO A на e : A (... : Eq A (run (f a)) e, run : IO A -> A, semantically) не заменить.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от anonymous

Бро! Ты мне так любимого поциента до кондратия доведёшь! Так нельзя! Ты, пожалуйста, впредь оперируй понятиями, понятными кухарке и дворнику. Ничто мало-мальски сложнее - недопустимо.

Заранее спасибо за понимание!

anonymous
()
Ответ на: комментарий от quasimoto

A выражение f a (: A) в конечном итоге можно заменить на e : A (refl : Eq A (f a) e)

Ну в том случае о котором он говорит, можно и для мутабельных переменных заменить. Обычное реактивное программирование.

anonymous
()
Ответ на: комментарий от anonymous

For example, in an imperative programming setting, a := b + c would mean that a is being assigned the result of b + c in the instant the expression is evaluated. Later, the values of b and c can be changed with no effect on the value of a.

In reactive programming, the value of a would be automatically updated based on the new values.

Что-то я не пойму всей глубины концепции. Функция a() = b + c при вызове a() будет меняться значениями вместе с b и c. Ну и ссылочной прозрачности нет никакой.

А если так

data Formula t = Formula t :+ Formula t | Cell (Ref t)

eval :: Num t => Formula t -> IO t
eval (Cell r) = get r
eval (x :+ y) = liftM2 (+) (eval x) (eval y)

b, c :: Ref Int
b <- ref 1
c <- ref 2

a :: Ref Int -> Formula Int
a x = Cell x :+ Cell b :+ Cell c

{-
> x <- ref 3
> let f = a x
> eval f
6
> x =: 4
> c =: 5
> b =: 6
> eval f
15
-}

то b и c это чистые адреса ссылок, a — чистая функция из адреса ссылки в формулу (которая просто ADT с такими адресами). А вот run . eval уже не ссылочно прозрачная.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Ну и ссылочной прозрачности нет никакой.

Все есть. В реактивном случае a+b это a()+b(), f = a + b => f = a() + b(), а заменить f() на a() + b() мы вполне можем.

anonymous
()
Ответ на: комментарий от quasimoto

Я вот сейчас подумал, что описанная конструкция выделения грязи из реактивного программирования в некотором смысле двойственна подходу хаскеля. Я помню у тебя была статья о категорной семантике ИО в хаскеле, можешь дать еще раз? Интересно, что получится, если для этой конструкции построить двойственную (ко-ИО)?

anonymous
()
Ответ на: комментарий от anonymous

Я говорю это ненужная софистика. a() возвращает разные вещи, b() возвращает разные вещи, f возвращает разные вещи, и f() тоже, f() которая возвращает разные вещи можно заменить на её код, то есть на a() + b() которые возвращают эти же разные вещи. Это называется inlining. Если f() вычисляется в один и тот же терм «a() + b()», то её можно на него заменить, она чистая, её eval в значения — нет.

Напиши на любом языке код с сигнатурами и всё станет понятно

int f(int(&a)(), int(&b)()) {
    return a() + b();
}

f не возвращает одни и те же значения для одних и тех же адресов. Для любых адресов адрес f остаётся тем же, разумеется :) f(a, b) можно заменить на a() + b(), но вызов любой «функции» можно заменить её телом, так что уточнений про a vs a() тут не нужно.

Пример чистой функции это такая f:

struct Add {
    int(&a)();
    int(&b)();
    int eval() { return a() + b(); }
};

Add f(int(&a)(), int(&b)()) {
    return { a, b };
}

она возвращает одинаковые структуры (по значению) для одинаковых адресов, но не Add::eval — разные значения для одной и той же структуры.

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

Тут же разговор про ЯП общего назначения вообще, естественно мы можем взять не вообще а выделить чистые Smth -> Expr от Expr -> Values в грязном Smth -> Values. Так кто-то может сказать, что eval :: Num t => Formula t -> IO t в хаскеле чистая в термы IO, а грязная run . eval в рантайме — если так проговорить, то путаницы нет, но иначе может быть потому что под eval может подразумеваться сразу run . eval.

Я помню у тебя была статья о категорной семантике ИО в хаскеле, можешь дать еще раз?

Про категорную не знаю (кроме слова monad), но посмотри

http://www.disi.unige.it/person/MoggiE/ftp/ic91.pdf

http://www.disi.unige.it/person/MoggiE/publications.html

http://www.staff.science.uu.nl/~swier004/Publications/Thesis.pdf

http://www.staff.science.uu.nl/~swier004/publications.html

http://research.microsoft.com/en-us/um/people/adg/publications/fpio.pdf

http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.16.3541

http://research.microsoft.com/en-us/um/people/adg/Publications/details.htm

http://research.microsoft.com/en-us/um/people/simonpj/Papers/papers.html#monads

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Я говорю это ненужная софистика.

Ну изоляция эффектов в ИО - тоже ненужная софистика.

Если f() вычисляется в один и тот же терм «a() + b()», то её можно на него заменить, она чистая

Ну какбы и все. f - чистая. Чего нам еще надо?

Напиши на любом языке код с сигнатурами и всё станет понятно

Но в нашем случае у нее сигнатура f: Integer -> Integer -> Integer

Пример чистой функции это такая f:

А в нашем языке нету eval. Так что мы просто не можем написать грязную функцию. Точно так же как в хаскеле не можем. И в точности по той же самой причине - потому что в хаскеле нету евала (unsafePerformIO).

anonymous
()
Ответ на: комментарий от quasimoto

Про категорную не знаю (кроме слова monad), но посмотри

Слушай, а ты же не ищешь эти статьи каждый раз по новому? У тебя, видимо, есть список как-то каталогизированный? Может ты им целиком и поделишься? Или вовсе можно сделать для этого отдельный тред и прикрепить - для faq по лиспу есть такой тред же.

anonymous
()
Ответ на: комментарий от anonymous

Ну какбы и все. f - чистая. Чего нам еще надо?

Это очень специальная чистая f. То есть «f=function(x)(+ x x)», бла-бла-бла, все функции итак чистые во всех языках. Тогда как не все, выясняется, а очень специальные (как в RP).

И всё это ненужный разговор — есть ясная и всем понятная терминология, будь то с изоляцией эффектов или без (и к любому языку теоретически можно приделать статический анализатор который будет делать то же самое, что система типов для хаскеля, исходя из исходных атрибутов pure/unpure для функций).

Но в нашем случае у нее сигнатура f: Integer -> Integer -> Integer

Нет, то есть непонятно на каком языке ты эту сигнатуру написал, явно не на хаскеле. Так что пиши сразу демонстрирующий код тогда — о чём ты говоришь.

А в нашем языке нету eval

А вообще есть.

Даже так — на любом языке можно написать предикат который проверяет (и тестирует для счётных/больших для перебора типов аргументов проверяемой функции) является ли переданная функция чистой (в обычном смысле) или нет. И на хаскеле тоже — нет run, но есть bind.

quasimoto ★★★★
()
Ответ на: комментарий от anonymous

У тебя, видимо, есть список как-то каталогизированный?

Нету.

Насчёт CoIO — язык IO описывается с помощью teletype functor, IO a это Free Typletype a, free monad, выполнение осуществляется с помощью foldFree (с помощью runAlgebra :: Teletype (ModelMachineTranslation a) -> ModelMachineTranslation a). Если обратить, cofree comonad, unfoldCofree, так что coexec :: (?) -> CoIO a вместо IO a -> ModelMachineTranslation a, corun :: (?) -> CoIO a?.. wut? :)

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от quasimoto

http://blog.sigfpe.com/2014/05/cofree-meets-free.html

I often think of coalgebraic things as machines. They have some internal state and you can press buttons to change that internal state.

state

O_o

Just as monads are a kind of generalised algebraic structure, comonads are a generalised kind of machine.

if you think of comonads as machines, monads give a language that can be used to compute something based on the output of the machine.

Монадические вещи (языки, структуры) конструируются монадическим интерфейсом и сворачиваются в значения катаморфизмами (свёртками), комонадические вещи (потоки, машины) генерируются анаморфизмами (развёртками), а их части обозреваются комонадическим интерфейсом. Если Free для ADT-функтора даст «язык», то, надо полагать, Cofree дуализация этого ADT-функтора даст соответствующую «машину», и наоборот.

Но

http://comonad.com/reader/2011/monads-from-comonads/

Is there is a similar construction that lets you build a comonad out of a monad?

Sadly, it seems the answer in Haskell is no.

for any more interesting such type that actually lets us get at its contents, we would be able to derive a circuitous path to unsafePerformIO!

Итого примеры — Identity (http://comonad.com/reader/2008/the-cofree-comonad-and-the-expression-problem/), дуализация — он же

Free Identity (Int -> Int) -> Cofree Identity Int -> Int
~~~~~~~~~~~~~~~~~~~~~~~~~~    ~~~~~~~~~~~~~~~~~~~
Nat                           Stream

выполнение числа («программы») на потоке («машине») возвращает такой-то элемент потока.

UpDown a = a + a, дуализация — Two a = a * a

Free Two (x -> r) -> Cofree UpDown x -> r
~~~~~~~~~~~~~~~~~    ~~~~~~~~~~~~~~~
FreeMagma            CoFreeComagma

Ещё с comonad стороны:

http://blog.sigfpe.com/2006/12/evaluating-cellular-automata-is.html

http://github.com/ekmett/machines/blob/master/src/Data/Machine/Moore.hs

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Это очень специальная чистая f. То есть «f=function(x)(+ x x)», бла-бла-бла, все функции итак чистые во всех языках. Тогда как не все, выясняется, а очень специальные (как в RP).

Да нет, она вполне обычно чистая, в соответствии с классическим определением чистоты. Так устроена семантика языка, что если f = x + y, то f везде можно заменить на x+y

Нет, то есть непонятно на каком языке ты эту сигнатуру написал, явно не на хаскеле.

Конечно, не на хаскеле. На обычном ЯП с реактивной семантикой.

anonymous
()
Ответ на: комментарий от quasimoto

выполнение числа («программы») на потоке («машине») возвращает такой-то элемент потока.

Ну как раз то о чем я и говорю. Вместо случая хаскеля (где мы строим некоторый грязный IO-язык так, чтобы хаскель сталь для него чистым мета-языком) мы в РП случае строим грязный метаязык для нашего чистого языка. И этот грязный метаязык будет иметь смысл некоторой машины внутри которой крутится чистый РП-терм

anonymous
()
Ответ на: комментарий от anonymous

Так устроена семантика языка, что если f = x + y, то f везде можно заменить на x+y

Так это везде так, но не называется чистотой. Надо заменять не f(a) на body_of_f_subst_arg_with_a, а f(a : A) : B на e : B где f(a : A) : B =eval=> e : B и e канонический.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Так это везде так, но не называется чистотой.

если f без аргументов, то именно это и называется чистотой (ссылочная прозрачность в этом случае эквивалентна чистоте). Ну и в любом грязном языке это не так.

Надо заменять не f(a)

Так у нас f, а не f(a), то есть нету аргумента который ты будешь евалить. В случае «f(a) = x + a» мы конечно можем везде заменить f(2) на «x + 2».

anonymous
()
Ответ на: комментарий от quasimoto

где f(a : A) : B =eval=> e : B и e канонический

если f = a + b, то a+b канонический и есть. Он дальше никуда не редуцируется, это окончательный результат, все.

anonymous
()
Ответ на: комментарий от anonymous

Так у нас f, а не f(a)

Нет, давай общий случай функций. Мы говорим про функции — чистые или нет. Про любые функции. «f без аргументов» это вообще непонятно что — обычно (C, C++ и т.д.) это вовсе не функция, а переменная или константа которая _должна_ инициализироваться единожды, так что вопрос о замене не уместен, иначе это будет R f(), ну и прочие функции R f(...) которые более-менее заменяются на свои тела будучи грязными или нет — твой критерий чистоты не работает.

В хаскеле, конечно, сразу чистота, так что никакой разницы, f это CAF, 0-арная функция, такая же прозрачная как всё остальное, кроме функций в IO непрозрачных с т.з. bind:

unsafePerformIO :: IO a ->  a
bindIO          :: IO a -> (a -> IO b) -> IO b
                   ~~~~     ~

http://en.wikipedia.org/wiki/Elm_(programming_language)

http://library.elm-lang.org/catalog/elm-lang-Elm/0.12.3/Signal

http://elm-lang.org/learn/What-is-FRP.elm

lift2 (/) Mouse.x Window.width

И в чём отличие от хаскеля?

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

В хаскеле, конечно, сразу чистота, так что никакой разницы

Точнее, скорее <- это однократная инициализация, f ~ f().

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от anonymous

Он дальше никуда не редуцируется, это окончательный результат, все.

Тут «a» тоже дальше терма Formula никуда не редуцируются — некоторые программы действительно могут быть чистыми функциями, да :)

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

И в чём отличие от хаскеля?

Ну так я же еще давно сказал, что это двойственная к хаскелю конструкция :)

И там и там применяется один и тот же метод «выталкивания» грязи из хост-языка, просто «выталкивается» она в другую стороную. И тот лифт идет не в монаду, а комонаду (Mouse.clicks - коданные, поток)

anonymous
()
Ответ на: комментарий от quasimoto

это вовсе не функция, а переменная или константа которая _должна_ инициализироваться единожды

Ну так она и инцициализируется единожды, как сигнал. Так же как у тебя readLn в хаскеле тоже инициализируется единожды при вычислении IO - как IO :)

anonymous
()
Ответ на: комментарий от anonymous

Если мы определяем функцию, скажем, f=function(x)(+ x x), с точки зрения функции, при вызове, например, f(а), она возвратит (+ a a), а чем является этот а в контексте вызова, функцию не волнует.

разве это не основная фича ФП?

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

реальность такова, что если есть нечто, что можно сломать, то это нечто рано или поздно будет сломано.

emulek
()
Ответ на: комментарий от quasimoto
int f(int(&a)(), int(&b)()) {
    return a() + b();
}

проблема только в том, что результат этой функции уже не передать в другую подобную функцию. Т.е. a()+b() работает, а вот a()+b()+c() — увы. Gcc скажет что первым параметром f() должен быть указатель на функцию, а ты пихаешь туда int.

Т.е. такой подход работает, но только один раз. Ну как с крыши многоэтажки спрыгнуть. Любой сможет, не надо быть суперменом.

emulek
()
Ответ на: комментарий от quasimoto
struct Add {
    int(&a)();
    int(&b)();
    int eval() { return a() + b(); }
};

Add f(int(&a)(), int(&b)()) {
    return { a, b };
}

и тут fail. Раз ты отдаёшь Add, то ты обязан принимать Add.

emulek
()
Ответ на: комментарий от anonymous

Так же как у тебя readLn в хаскеле тоже инициализируется единожды при вычислении IO - как IO :)

Ну когда ты делаешь

f = readLn

some = do
    ... f ...
      ... f ...
  ... f ...

то это 0-функция — можно заменять, в любом случае будет читать столько раз, сколько используется, тогда как

x <- readLn

some = do
    ... x ...
      ... x ...
  ... x ...

есть инициализация и чтение единожды с использованием результата (и let для чистых без эффектов, просто sharing).

Ну так я же еще давно сказал, что это двойственная к хаскелю конструкция :)

import Data.List
import Control.Monad
import Test.QuickCheck
import Test.QuickCheck.Monadic

propPure :: (Show a, Arbitrary a, Eq b) => Int -> (a -> IO b) -> Property
propPure n f = monadicIO $
  assert . (==1) . length . nub =<< replicateM n . run . f =<< pick arbitrary

someTest :: IO ()
someTest = quickCheck $ propPure time someFun

либо такой тест пишется в языке и у всех всё как у всех. А если не пишется, то это какой-то особый язык и про него нужно отдельно говорить, не мешая понятия «чистый» и «не чистый» у всех остальных (в т.ч. в хаскеле).

quasimoto ★★★★
()
Ответ на: комментарий от emulek

Ещё не понял как делать (глобальное) состояние в ФП (сохраняя ссылочную прозрачность)? Есть разные способы :) Ещё раз — State (= StateT s Identity, так же StateT s IO и т.п.); ST, ST[U]Array, STRef и т.п.; IO, IO[U]Array, IORef (+ Chan, MVar, QSem[N], + STM — TArray, TBQueue, TQueue, TChan, TMVar, TSem, TVar, ...) и т.п. (типа vector); просто передача мутабельных ссылок на эти вещи между функциями, top-level <- на конструкторе для них или трансформер с ними над IO.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 2)
Ответ на: комментарий от emulek

Т.е. a()+b() работает, а вот a()+b()+c() — увы. Gcc скажет что первым параметром f() должен быть указатель на функцию, а ты пихаешь туда int.

ЩИТО

Раз ты отдаёшь Add, то ты обязан принимать Add.

это за бред?

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

Он говорит о том, что у тебя + - не операция (в математическом смысле).

anonymous
()
Ответ на: комментарий от quasimoto

тогда как

И в случае тогда как тоже можно смело заменять :)

либо такой тест пишется в языке и у всех всё как у всех. А если не пишется, то это какой-то особый язык и про него нужно отдельно говорить, не мешая понятия «чистый» и «не чистый» у всех остальных (в т.ч. в хаскеле).

Ну да, особый - как IO. В хаскеле есть метаязык (собственно хаскель) и ИО-язык. В рп есть язык (Signal в случае емл) и метаязык (емл). При помощи комонад можно инкапсулирвать сайд-эффекты точно так же как и при помощи монад, по сути это один и тот же подход, только «наоборот»: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.46.5169&rep=rep1...

anonymous
()
Ответ на: комментарий от anonymous

И в случае тогда как тоже можно смело заменять :)

x на readLn нельзя, x <- readLn; use x; use x again это readLn :: _IO_ Value >>= \(x :: _Value_) -> use x >> use x again — и по типу нельзя и как lift use readLn >> lift use readLn again нельзя по смыслу.

Ну да, особый - как IO.

Ничего он не особый. Кроме как на лоре всю эту фигню про ио термы и не услышать нигде :) Ну почти, разные бумажки могут вводить в залужение, конечно.

И вообще «если не пишется, то это какой-то особый язык», «Ну да, особый - как IO», при том что я выше показал как пишется — bind твои «мета-ио-языки» смешивает и на деле на хаскель принято смотреть с точки зрения полной семантики без разделений.

Он говорит о том, что у тебя + - не операция (в математическом смысле).

int operator+(int, int); это операция в математическом смысле.

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

x на readLn нельзя

Ну потому что тип не совпадает, да. Но не потому что readLn возвращает что-то другое при разных вызовах.

Ничего он не особый.

Особый, как и ИО. Внутренний язык.

И вообще «если не пишется, то это какой-то особый язык»

Не понял, что не пишется?

на хаскель принято смотреть с точки зрения полной семантики без разделений.

У хаскеля нету никакой такой «полной» семантики :) Есть семантика хаскеля как метаязыка, который генерирует ИО-терм. На этом семантика заканчивается. То есть в теории дальше должна быть семантика ИО-языка, но ее пока никто не описал.

bind твои «мета-ио-языки» смешивает

Ничего он не смешивает. На деле стандарт хаскеля даже не специфицирует как работает bind и как представлено IO. По факту тебе никто не мешает представлять ИО-термы каким-нибудь реальным языком (пехепе каким-нибудь :)) а биндом отдавать куски на выполнение интерпретатору. И это будет вполне хаскель - никакого нарушения стандарта (если не учитывать какие-то особенности рантайма, но это уже вопрос их моделирования)!

Факт в том что рп-подход к изоляции сайд-эффектов ничем существенно не отличается от подхода хаскеля (он по факту _двойственный_, основан на тех же принципах и той же теории), так что твои кукареки типа «это софистика блаблабла» - не роляют. То есть я согласен что вся эта «изоляция» есть заметание мусора под ковер и никакой хаскель на самом деле не чистый. Но ты то считаешь иначе? Так что тебе следует быть последовательным и признать что рп-вариант так же чист как и в случае хаскеля.

int operator+(int, int); это операция в математическом смысле.

int operator+(int, int); - Конечно, операция. А то, что написал ты - не операция.

anonymous
()
Ответ на: комментарий от anonymous

Есть семантика хаскеля как метаязыка, который генерирует ИО-терм.

То есть да, ты прав, никто не говорит что полученное в результате исполнения программы на хаскеле ИО - это действительно терм некоторого языка, считается что просто оно возвращает некую невменяемую ИО-хуйню, которая неизвестно что из себя представляет. Но на нее _удобно_ смотреть как на внутренний язык и такой подход вполне осмысленен (все-таки это ИО - по смысле некое вычисление, почему бы на не считать что оно представлено как программа на некотором внутреннем языке? хотя в реализации этого никаког овнутреннего языка нету, понятное дело). И это куда лучше, чем считать результат непонятной ИО-хуетой, согласись? :)

anonymous
()
Ответ на: комментарий от anonymous

Но не потому что readLn возвращает что-то другое при разных вызовах.

Он возвращает разные вещи. То есть run readLn, говорю — никому не интересны IO-термы, кроме как при просмотре Core, а то так и в C функции тоже могут выдавать один и тот же ассемблер. Аналогично readLn >>= f >>> return загоняет в _чистую_ функцию f _разные_ вещи, элементарно же, это проверяется.

Не понял, что не пишется?

Глобальное состояние в ФП (комментарий) — propPure и someTest пишутся и работают, это я _определяю_ как обычность и возможность разделять чистые функции и остальные (те и другие присутствуют) с точки зрения полной семантики без всяких разделений. Что в хаскеле, что где-то ещё всё это есть.

У хаскеля нету никакой такой «полной» семантики :)

А ну да, он святым духом работает как работает (ожидаемо и однозначно, как любой другой язык). На IO термах она не заканчивается (можешь хоть про Core с STG и C-- бумажки почитать, хоть уяснить, что есть конкретная реализация GHC которая работает, так что у неё не может не быть семантики, а абстрактно стандарт и гипотетический PHP интерпретатор это как-то не очень интересно :)).

Но ты то считаешь иначе?

Нет, я же объясняю что bind это ограниченная форма run, которая позволяет смешивать грязь с чистыми функциями — ну вот как в C++ a() + b() — чистый плюс принимает значения грязных функций, так что всё выражение становится нечистым, liftM2 (+) a b, если пойти дальше IO термов, то ясно, что с точки зрения выполнения мы будем получать разные вещи.

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

int operator+(int, int); - Конечно, операция. А то, что написал ты - не операция.

Давай я поясню, первый вариант f не чистый, он превращается в чистый разделением — новый вариант чистый и просто возвращает структуру (по сути это f = id, структуру дальше вычислять некуда), которую дальше уже может вычислить нечистым eval в int. Это как бы пример того что ты начал обсуждать, но я не вижу тут чего-то неожиданного.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Так оно вполне конкретно выглядит как танцы с RealWorld в Core (-ddump-simpl) — http://www.scs.stanford.edu/11au-cs240h/notes/ghc-slides.html.

Ну это оно в одной конкретной реализации так выглядит. В данном случае вычисление представляется как лямбда, через которую протаскивается World, но как реализована эта лямбда? По сути вопрос «как представлена ИО» заменяется эквивалентным «как представлена лямбда с World». Но от перемены слов згачение не меняется, в данном случае :)

И нам все еще удобно и естественно представлять вычисление как нечто выраженное на каком-либо языке. Во-первых, это не противоречит спецификации хаскеля, во-вторых это соответствует реализации (даже если это лямбда, как в случае GHC, то она представлена как терм на машкоде).

anonymous
()
Ответ на: комментарий от quasimoto

Он возвращает разные вещи.

Нет, конечно.

То есть run readLn

Но в хаскеле нету никакого «run readLn». Нельзя в хаскеле такое написать. Это не код на хаскеле.

никому не интересны IO-термы

Интересно или неинтеренсо - но такова семантика хаскеля. И именно из-за этого (что программы на хаскеле не совершают никаких сайд-эффектов, а занимаются только генерацией некоего ИО-терма на внутреннем языке, который будет выполнен неспецифицированным вычислителем) хаскель считается чистым языком. Формально. Да, это софистика.

propPure и someTest пишутся и работают

Что за propPure и someTest? Что они делают? Ты действительно считаешь, что я вот этот кусок говнокода:

propPure n f = monadicIO $ assert . (==1) . length . nub =<< replicateM n . run . f =<< pick arbitrary

буду парсить?

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

Нет, не присутствуют. Еще раз - семантика хаскеля заканчивается на генерации IO. Все, что может делать хаскель в соответствии со своей семантикой - генерировать IO. Все. Дальше хаскель ЗАКАНЧИВАЕТСЯ. Дальше хаскеля НЕТ. Вернули IO - хаскель кончился. Куда дальше пойдет это ИО и кто и что с ним будет делать - никого не ебет. На самом деле программа на хаскеле вообще может спокойно НИЧЕГО НЕ ДЕЛАТЬ. Это не будет нарушением семантики хаскеля. Не существует никакой «полной» семантики. Все, что происходит до ИО (и заканчивается на его генерации) - это и есть полная семантика хаскеля. И разделение в хаскеле есть просто by design.

А ну да, он святым духом работает как работает

Он работает как работает потому что так ДОГОВОРИЛИСЬ. Но в _семантике_ языка эти аспекты никак не отражены.

На IO термах она не заканчивается

Заканчивается.

(можешь хоть про Core с STG и C-- бумажки почитать, хоть уяснить, что есть конкретная реализация GHC которая работает

Это конкретная реализация. Не надо путать ЯЗЫК и его РЕАЛИЗАЦИЮ. У хаскеля есть семантика вплоть до генерации ИО. Но если мы хотим чтобы программа делала чтото полезное - то нам надо придумать какую-то семантику для, собственно, ИО. Вот каждая реализация и придумывает СВОЮ СОБСТВЕННУЮ семантику для ИО. Но это уже не имеет абсолютно никакого отношения к хаскелю.

Нет

То есть хаскель - грязный язык?

Кроме того, unsafePerformIO

Понятно, что наличия unsafePerformIO мы не учитываем, так как с ним язык становится грязным.

новый вариант чистый и просто возвращает структуру (по сути это f = id, структуру дальше вычислять некуда)

Ну в том и дело что твоя f неправильная. Она принимает ссылки на инты, а возвращает структуру.

anonymous
()
Ответ на: комментарий от quasimoto

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

mix_mix ★★★★★
()
Ответ на: комментарий от anonymous

буду парсить?

Это многократный вызов f someValue, f anotherValue и т.д. для некоторого сгенерированного набора значений, с проверкой что все вызовы возвращают одно и то же для одинаковых аргументов — если да, то тест пройден и функция скорее всего чистая, если нет — она точно грязная. И run не нужен, bind делает внутри тот же run, его достаточно, картинка, ещё напоминаю про readLn >>= f >>> print. Ну и run тоже как бы есть (unsafePerformIO это стандарт, http://stackoverflow.com/a/3124722, а в GHC вовсе можно сделать import GHC.Types и делать с IO что хочешь).

Она принимает ссылки на инты, а возвращает структуру.

Это ссылки на функции, а не на инты. И что значит неправильная? Одни и те же функции — одна и та же по значению структура.

То есть хаскель - грязный язык?

То есть хаскель это язык в котором явно разделены два слоя — слой чистых функций и слой нечистых, из первого можно попасть во второй, но не наоборот, кроме как с помощью unsafePerformIO (см. выше на SO картинку), семантика должна быть полной и говорить нужно не только о том как хаскель транслируется в core (ну серьёзно — почему не прицепиться к С, мол, у него семантики нет, он в непонятно что транслируется, ассемблеров много, или вообще может интерпретироваться на PHP, вместо pure/unpure можно привязаться к const/non-const — те тоже два слоя с погружением в одну сторону и небезопасными кастами в другую, также можно ноги отстрелить), а о том что вообще происходит, в смысле эффектов и значений которые мы получим при выполнении и того как программы конкретно работают. Это не то чтобы «договорились», просто без всяких реализаций ясно, что любой корректной программе на хаскеле соответствует вполне однозначные результаты и эффекты, а если брать что-то вроде STG, то и исполнение.

Еще раз - семантика хаскеля заканчивается на генерации IO.

Как-то если посмотреть на публикации (Moggi, Gordon, Swierstra, SPJ на тему STG — посмотри, там мотивация не только в необходимости этого для реализации, но и для операционной семантики) — ни у кого ничего не заканчивается.

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от mix_mix

Источник это ключевые слова moggi+monad, functional+specification+effects, lazy+io+operational+semantics, spj+publications, которые у меня с темой семантики IO ассоциируются — как-то нечего выкладывать :)

quasimoto ★★★★
()
Последнее исправление: quasimoto (всего исправлений: 1)
Ответ на: комментарий от quasimoto

Т.е. a()+b() работает, а вот a()+b()+c() — увы. Gcc скажет что первым параметром f() должен быть указатель на функцию, а ты пихаешь туда int.

ЩИТО

написать можно любую ☣. Но с какой целью ты это написал? Тут ходит твой будущий работодатель, и ты своей скильностью хвастаешься?

Раз ты отдаёшь Add, то ты обязан принимать Add.

это за бред?

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

emulek
()
Ответ на: комментарий от mix_mix

И прикрепить темку как FAQ по лиспу. Было бы вельми полезно. И потом если что можно будет на этот прикрепленный тред ссылаться.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.