LINUX.ORG.RU

Концептуальная дыра ФП.

 , , ,


1

2

«Изменяясь, оно остается неподвижным»

Гераклит.

На протяжении нескольких месяцев уже я пытаюсь понять ФП. И пока у меня 2 варианта. Либо я идиот, либо ФП имеет огромную концептуальную трещину в самом основании.

Древние говорили, что нельзя дважды войти в одну и ту же реку. Это значит, что река - это всегда река. Река постоянно меняется, но она остается все той же рекой, и о ней можно говорить как о реке. Что мы имеем в ФП:

(define river-state 1)
(define fu (let ((the-river-state river-state)) (lambda() the-river-state)))
(write (fu))
(define river-state 2)
(write (fu))
(define river-state 3)
(write (fu))
;111
Благодаря лексической области, мы не можем рассуждать о реке как просто о реке, мы (и вызывающий код) вынуждены говорить только о той реке, которая была и которая зафиксирована. У нас здесь вообще нет реки в обычном понимании. Здесь нет абстракции.

Дело тут даже не во времени. Мы вообще лишены возможности рассуждать о вещах в общем смысле. ФП снижает абстракцию. Абстргирование стремиться к нулю. Только ради компиляции мы вынуждены мириться с тем, что наш язык не дает нам возможности расуждать о вещах наиболее естественным путем.

PS Как выяснилось, многие не поняли о чем я говорю. Я говорю здесь о фиксации состояния внутри ф-ции. То что код примера императивен я в курсе, это было сделано преднамеренно, для демонстрации идеи. Не надо капитанствовать по этому поводу.



Последнее исправление: anonimous (всего исправлений: 2)

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

то ты там хотел сказать со своей рекой на лиспе, при чём тут ФП и особенно Смолток.

Основная идея смоллтока - экстремально позднее связывание. Это динамика. Наша функция в стартовом топике - это пример раннего связывания: при вызове она ищет значение свободной переменной в лексическокм скопе. Это противоречит идеям смоллток.

под вхождением в реку дваджы в этом случае?

То, что концептуально у нас есть 2 реки: снапшот реки, и река вообще. Я говорю, что такая идея как «река вообще», очень трудновыразима в рамках чистого ФП.

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

Основная идея смоллтока - экстремально позднее связывание. Это динамика. Наша функция в стартовом топике - это пример раннего связывания: при вызове она ищет значение свободной переменной в лексическокм скопе. Это противоречит идеям смоллток.

Когда речь идёт о связывании в смолтоке, имеется в виду реакция объекта на приходящие сообщения.

В своём коде ты создал let-ом копию(?) переменной и возвращаешь её из лямбды. Где связь? :)

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

let-write-define. Чистая процедурная императивщина. При чем тут, спрашивается лисп как таковой? А не при чем.

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

А смолток тоже ФП, хуле

Кей заимствовал идею у lisp-1.5. Семантически, смоллток - это модифицированный лисп Маккарти, это Ъ-ФП, в том, старом смысле. ФП с динамикой и fexprs.

anonimous
() автор топика

чувак ты просто оверквалифед для этой вселенной

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

имеется в виду реакция объекта на приходящие сообщения.

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

anonimous
() автор топика

Ты уже утомил своими скобками засирать единственный стоящий внимания тег на лоре.

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

Реализация кондишенов в духе фп представляет из себя что-то вроде этого.


(define if_ (lambda(a b c) (a b c)))
(define true (lambda(x y) x))
(define false (lambda(x y) y))

(write (if_ true 1 2)); -->
(write (if_ false 1 2)) -->

Это кривая реализация, поскольку энергичныйпорядок вычислений не позволяет не вычислять аргументы. А поскольку ненужный аргумент может вычисляться сколько угодно долго, это не айс, и в лиспах кондишены реалаизованы в виде спец форм или макросов, т.е. Это синтаксис. К ФП это не имеет отношения. Алан Кей не был доволен этой ситуацией. Он говорил о том, что вычислять аргументы вообще всегда нужно в последний момент, и связывать тоже. Так что Ваш пример не говорит о непосредственной связи ФП со Смоллтоком. Однако, смолток взял идеи из олдскульного ФП, в первую очередь - fexprs, поэтому да, он наследник ФП, только не того ФП, что мы имеем сейчас.

anonimous
() автор топика
Ответ на: комментарий от yoghurt

Понять про условия для меня было сложновато.

Всё из-за этого

So we evaluate condition to either true or false, send it the #ifTrue:ifFalse: message, and we’re done.

Не привык я что к какому-то там true можно ещё и мессаджи слать и что-то там получать в ответ.

В supercollider (там реализован тот же smalltalk, только в профиль) похожий механизм.

true.if {1} {2} => 1
false.if {1} {2} => 2

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

Реализация кондишенов в духе фп представляет из себя что-то вроде этого.

...

Это кривая реализация, поскольку энергичныйпорядок вычислений не позволяет не вычислять аргументы. А поскольку ненужный аргумент может вычисляться сколько угодно долго, это не айс, и в лиспах кондишены реалаизованы в виде спец форм или макросов, т.е. Это синтаксис. К ФП это не имеет отношения. Алан Кей не был доволен этой ситуацией. Он говорил о том, что вычислять аргументы вообще всегда нужно в последний момент, и связывать тоже. Так что Ваш пример не говорит о непосредственной связи ФП со Смоллтоком. Однако, смолток взял идеи из олдскульного ФП, в первую очередь - fexprs, поэтому да, он наследник ФП, только не того ФП, что мы имеем сейчас.

Т.е. читая SICP, ты пропустил про нормальный порядок вычислений и ФП?

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

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

anonimous
() автор топика

Каждый раз читая твои посты не могу понять две вещи. Почему ты не можешь изъясняться нормальным языком? Зачем ты вообще это спрашиваешь(зачем создаешь проблему из ни*уя)?

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

1) Шизофазия

2) Тоже шизофазия

Давно ж все ясно с пациентом.

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

Да, и кстати, если б не замыкания, мы бы и так могли легко реализовать, без нормального порядка.

;плохая версия:
(define a 1)

(define if_ (lambda(a b c) (a b c)))
(define true (lambda(x y) x))

(write (if_ true a (write 'long-computing))); --> long-computing 1

;улучшенная версия, которая вроде работает как надо:
(define if_ (lambda(a b c) (a b c)))
(define true (lambda(x y) (eval x)))

(write (if_ true 'a '(write 'long-computing))); --> 1

;а вот-тут из-за калоши она фейлится:
(define tst (let ((a 10)) (lambda(x) (if_ true x 'foo) )))

(write (tst 'a)) --> 1

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

Да, и кстати, если б не замыкания, мы бы и так могли легко реализовать, без нормального порядка.

Замыкания тут не при чем. man eval.

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

Это связано с замыканиями, потому что если разрешить эвал в локальном контексте, вся семантика и оптимизация полетит к чертям. Иначе они бы не запрещали.

anonimous
() автор топика
Ответ на: комментарий от vonenij

Окей, продолжаю. Все эти let'ы - это не фишки лиспа самого по себе.

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

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

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

А сам лисп - это, скажем так, такой гомоиконный конструктор языков. Чуешь?

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

потому что если разрешить эвал в локальном контексте, вся семантика и оптимизация полетит к чертям.

Не знаю, что там с оптимизацией, но про семантику чушь полная. Посмотри реализацию eval в том же SICP. С замыканиями, да.

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

Вообще, let, это небольшой сахарок вот такокго плана.

(define tst1 ((lambda(a) (lambda() a)) 1))
(define tst2 (let ((a 1)) (lambda() a)))
(write (tst1)); --> 1
(write (tst2)); --> 1
Неважно, есть в реализации слово let или нет, сам let все равно будет. Другое дело, что let не обязан быть лексическим.

anonimous
() автор топика
Ответ на: комментарий от vsn

это не фишки лиспа самого по себе.

Определение лиспа в студию.

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

С замыканиями, да.

Да может быть локальный евал с замыканиями, не спорю. В JS так оно и есть. Ну может неправильно выразился, не семантику, аидеологию, я хз. Локальный eval - потеря ссылочной прозрачности.

anonimous
() автор топика

На протяжении нескольких месяцев уже я пытаюсь понять ФП

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

Понял как работает fold? Отличненько! Вешай себе плашку «Я знаю это ваше ФП». Уяснил чем отличается левый fold от правого? Дык вааще эксперт. Я *абсолютно* серьезно, без подначек. Потому что ничего другого использовать не получится. Тебе придется в одно рыло реализовывать добрую половину компилятора того же хаскеля (или ML, если хочешь), и добрую половину его же рантайма, попутно вникая не в очень удобоваримые чисто-математические (т.е. абсолютно бесполезные на практике, если не занимаешься исследованиями в области компиляторостроения) теории.

Ты пытаешься уцепиться за голимую теорию... Теория — фигня. Идиоматика важнее. Почему думаешь так популярно ООП? Да потому что еще с лохматых 90-х годов наработан охрененный идиоматический аппарат. Начиная от Коплина, через GoF и Александреску, заканчивая... (Чем там? Извините, не так подробно теперь слежу). А уж такие товарищи как Одерский... Но, парадокс, все на Одерского клали.

Сам же писал в очередном своем высере о «вреде» теории.

А в тех областях где ты сейчас ищешь, все что ты найдешь — теорию 60-летней давности, и идиоматику 30- (или все-таки 40-???) летней.

Вся идиоматика сейчас — вертится вокруг хаскеля (как некоторое время назад вертелась вокруг ML). Хочешь вариться в околоФПшном мире? Грызи хаскель. Не потому что хаскель крут, а потому что на данный момент он — lingua franca. Хотя, лучше использовать порядком подзабытый термин «аппликативное программирование» вместо избитого и неправильного «ФП».

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

В ФП только одна река. Всегда одинаковая - и в прошлом и в будущем. Она не может поменяться.

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

Это кривая реализация, поскольку энергичныйпорядок вычислений не позволяет не вычислять аргументы.

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

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

просто надо обернуть аргументы лямбду и форсировать их внутри if когда нужно

Если я правильно понял, как то так:


(define if_ (lambda(a b c) (a b c)))
(define true (lambda(x y) x))
(define false (lambda(x y) y))


(write ((if_ true (lambda() 1) (lambda() (write 'long_computing))))); --> 1
(write ((if_ false (lambda() 1) (lambda() (write 'long_computing))))); --> long_computing

?

Чем то напоминает eval.

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

По крайней мере, когда мне захотелось изучить функциональщину, я начал на ней писать, а не срать на форумы «Концептуальными дырами».

Дело тут даже не во времени. Мы вообще лишены возможности рассуждать о вещах в общем смысле. ФП снижает абстракцию. Абстргирование стремиться к нулю. Только ради компиляции мы вынуждены мириться с тем, что наш язык не дает нам возможности расуждать о вещах наиболее естественным путем.

Ну не похер ли тебе. Не нравится - не пиши. Используй плюсы, жабу, питон, ну или что там для тебя «Концептуально верное». Оставь ФП любителям ФП. Твои претензии - сплошное словоблудие.

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

Ну не похер ли тебе

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

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

вот так:

(define true (lambda(x y) (x)))
(define false (lambda(x y) (y)))



> Чем то напоминает eval.

Да. Только работает.
anonymous
()

Гераклит не говорил «Изменяясь, оно остается неподвижным».

vzzo ★★★
()

У автора концептуальная дыра в ДНК, тред можно закрывать.

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

Судя по твоей динамике, ты дальше хеллоу ворлда не пойдешь

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

Локальный eval - потеря ссылочной прозрачности.

Лол, а она-то тут при чем?

korvin_ ★★★★★
()

У тебя тут дело не в ФП совсем. Например, такое же поведение:

#include <cstdio>

int main()
{                                       // scope #1

    int rs = 1;                         // object #1
    int fu = rs;                        // copy constructor, object #2
    int& fur = rs;                      // reference to object #1
    printf("%d %d\n", fu, fur);
    {                                   // scope #2

        auto rs = 2;                    // object #3, new name in new scope
        printf("%d %d\n", fu, fur);
        //                ^ still #2
        //                    ^ still & to #1
    }

    rs = 2;
    printf("%d %d\n", fu, fur);
}

/*
1 1
1 1
1 2
 */

Дело в именах и их областях видимости, том как объекты этих имён размещаются в памяти и ссылаются друг на друга в случае объектов-ссылок, возможности или невозможностью поменять объект in-place в памяти на которую завязано данное имя.

В Haskell:

{-# LANGUAGE ScopedTypeVariables #-}

import Prelude hiding ( lookup )
import Data.Map
import Control.Applicative hiding ( empty )
import Control.Monad.State
import Data.IORef

-- * State monad.

type River = StateT Int IO ()

t1 :: River
t1 = do
  
  let wrt = (liftIO.print=<<)
  
  put 1
  wrt get
  put 2
  wrt get

t2 :: IO ()
t2 = evalStateT t1 undefined
-- 1
-- 2

-- * State monad over immutable map.

type NameMap = StateT (Map String Int) IO ()

t3 :: NameMap
t3 = do
  
  let def = (modify.).insert
      lkp = (<$>get).lookup
      wrt = (liftIO.print=<<)

  def "river-state" 1
  let fu = let trs = lkp "river-state" in trs
  wrt fu
  def "river-state" 2
  wrt fu

t4 :: IO ()
t4 = evalStateT t3 empty
-- Just 1
-- Just 2

-- * Reference in IO monad.

t5 :: IO ()
t5 = do
  rs :: IORef Int <- newIORef 1
  print =<< readIORef rs
  writeIORef rs 2
  print =<< readIORef rs
-- 1
-- 2

-- * Something else?

t6 :: IO ()
t6 = do
  let rs = 1 :: Int -- <- one object
      trs = rs      -- <- its copy or immutable/const reference to it
  print trs
  let rs = 2 :: Int -- <- another object! new scope/shadowing for its name
                    --    nothing to do with `rs' or `trs'
  print trs
-- 1
-- 1

где последний вариант демонстрирует такое же (и абсолютно нормальное) поведение как в твоём примере или в коде на C++ выше, предпоследний — явную мутабельность (видимо, то что ты хотел), а остальное — её чистые заменители.

quasimoto ★★★★
()

этот фимозник добрался и до девелопмента... какая жесть... :(

Rastafarra ★★★★
()

Древние говорили, что нельзя дважды войти в одну и ту же реку.
Это значит, что река - это всегда река.

Какое любопытное правило вывода у вас.

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

У тебя тут дело не в ФП совсем

Дело не в ФП. Дело в стиле ФП.

anonimous
() автор топика

let

а при чём тут ФП?

То что код примера императивен я в курсе, это было сделано преднамеренно, для демонстрации идеи. Не надо капитанствовать по этому поводу.

это не дыра. Это говнокод и забивание гвоздей отвёрткой. Точно такую же «трещину» можно «продемонстрировать» и в C++11, только уже наоборот — «какое же говно ваш ИП, если на нём можно сделать лямбду!»

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