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 ★
()
Вы не можете добавлять комментарии в эту тему: топик перемещен в архив.