LINUX.ORG.RU

Императивность Scheme

 ,


0

2

Правильно ли я понимаю, что наличие мутабельных переменных (set! и компания) позволяет отнести Scheme/Lisp к императивным языкам? Из-за мутабельности вызовы функций могут иметь сайд-эффекты, становится важен порядок вызовов, ну и далее вырастает весь комплекс проблем классических императивных процедурных языков. Если это рассуждение не правильно, то объясните пожалуйста в развернутой форме, что именно не так?

★★★★★

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

Да, их не существует.

а о чём тогда вообще разговаривать?

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

Еще раз. Исходная фраза была следующей:

И почему два разных вызова функции для получения даты/времени возвращают разный результат.

Оба вызова функции getTime :: IO Time вернут два абсолютно одинаковых вычисления. Аналогично, оба вызова функции setTime :: Time -> IO () вернут два абсолютно одинаковых вычисления.

Совершенно неясно, что за состояние ты вообще увидел, и где.

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

вернут два абсолютно одинаковых вычисления

Ну, вычисления эти меняют состояние, когда вычисляются?
Результаты вычислений могут быть разными?

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

А ты думаешь, ему больше вот делать больше нефига?

shamaz
()

Ну вот, пришел maxcom и зарезал анонимусов.

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

через unsafePerformIO или аналог

единственное применение unsafePerformIO, подразумевающееся в стандарте - FFI к чистым функциям

Анонимус говорил про явный вызов в программе unsafePerformIO. А к аналогам относится и, например, вызов результата функции main, который производится runtime'ом после её вычисления.

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

вычисления эти меняют состояние, когда вычисляются

Они вычисляются уже после выполнения программы на Haskell.

Считай, что функция getTime тебе возвращает текст «посмотри на часы». Результат этого просмотра будет разным в разные моменты времени, но текст один и тот же.

Таким образом, любая программа на Haskell является всего лишь компилятором из исходников Haskell в вычисление сторонних эффектов (результат функции main).

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

Таким образом, любая программа на Haskell является всего лишь компилятором из исходников Haskell в вычисление сторонних эффектов

чёта не понял. Извините за тупость. А любая программа на С++ является всего лишь компилятором из исходников С++ в вычисление сторонних эффектов?

Bad_ptr ★★★★★
()

Иппать! Нет никакой разницы императивный язык или не императивный!

Когда я из хаскеля вызываю либу на C (а это 99% вызовов, ибо рантайм и остальное окружение написаны на C), это как? Императивненько или не императивненько?

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

Причем, в случае хаскеля, значительно проще написать обертку на C, чем дергать то же самое через FFI. В особенности, это касается всяких Winapi.

Лисповцам не в пример проще. У них макросистема и ассемблер из коробки. Например, тот же COM дергать — одно удовольствие. А веcь .NET с кучей весьма годных библиотек, в первую очередь DCOM сервер.

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

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

Ты понимаешь, что если ты делаешь print в несущеcтвующий файл, то программа повалится?

Теорему Геделя еще можно не понять, но это уже верх банства. Я уж молчу про твои постоянные оскорбления участников форума.

alg0rythm
()
Ответ на: комментарий от Bad_ptr
data Time

instance Num Time

instance Show Time where
  show = const "<some function of time>"

data IO :: * -> * where
  GetTime :: IO Time
  SetTime :: Time -> IO ()
  (:>>=)  :: IO a -> (a -> IO b) -> IO b

instance Monad IO where
  (>>=) = (:>>=)

instance Show (IO a) where
  show GetTime = "<returns Time generator>"
  show (SetTime t) = "<returns setter for time t = " ++ show t ++ ">"
  show (gen :>>= user) = "<returns an action via gen . user combination " ++
    "(Kleisli categories bla-bla-bla) where gen = " ++ show gen ++
    ", user = " ++ show (user undefined) ++ ">"

main' :: IO ()
main' = do
  x <- GetTime
  y <- GetTime
  SetTime (y - x)

{-
> main'
<returns an action via gen . user combination (Kleisli categories bla-bla-bla) where gen = <returns Time generator>, user = <returns an action via gen . user combination (Kleisli categories bla-bla-bla) where gen = <returns Time generator>, user = <returns setter for time t = <some function of time>>>>
-}
quasimoto ★★★★
()
Ответ на: комментарий от Bad_ptr

В do { x <- GetTime; y <- GetTime; SetTime (y - x) } GetTime чисто добывает всегда один и тот же генератор времени (генератор, то есть канал на выход всегда один, данные в нём нас не интересуют), (>>=) чисто отправляет выхлоп генератора на вход другого действия, локально очищая его, то есть для одних и тех же генераторов (каналов на выход) и действий берущих выхлоп (каналов со входом и выходом) составляет одни и те же генераторы (как схему из таких каналов в виде очевидной композиции), SetTime для разных времён (как чистых функций от того что у нас доступно от входов текущих действий — они уже локально очищены) чисто возвращает один и тот же сеттер (именно этого) времени (то есть нечто только со входом, приёмник). Или иначе говоря, конфигурация каналов данных чистая (генераторы, приёмники, композиции — (>>=) это \m f -> (const m >=> f) () где (>=>) это композиция в Kleisli категории функций из очищенных входов в генераторы, сама такая композиция проводит очищение по мере своей работы, это нужно чтобы держать неизвестные данные в одном монадическом слое, не выпускать их наружу, но уметь обрабатывать их с помощью чистых функций), сами данные могут быть любыми, дальше, на уровне ниже чистого языка, всё это можно интерпретировать обычно — SetTime и GetTime это обычные императивные функции с эффектами, >>= это создание окружения и ;, ну и все чистые вычисления при этом надо проводить.

Разумеется, это я всё только что придумал, но это нормально для хаскеля — каждый раз придумывать семантику IO :)

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

Опять простите за тупость. Т.е. Вы хотите сказать, что на языке хаскель все функции таки являются чистыми, т.е. на одинаковый вход выдают одинаковый выход. Даже установка/взятие времени. А достигается это тем, что язык хаскель компилируется потом в какой-то более низкий язык, где уже происходит императивщина во все поля.
Но ведь язык С++ тоже не вычисляет никаких функций, вычисление происходит на более низком уровне. Он сначала компилиться в асм, а вот в асме то и происходят все непотребства.
Не?

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

С++ тоже не вычисляет никаких функций, вычисление происходит на более низком уровне

В сях команды не делятся на чистые и нечистые ни на каком уровне.

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

В сях команды не делятся на чистые и нечистые ни на каком уровне.

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

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

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

Нет никаких «тех пор». Есть программа на Си. Есть результат компиляции этой программы в машинный код — программа на машинном коде. Это разные программы на разных языках.

С точки зрения программы на Хаскелле никаких побочных эффектов нет. Каким образом эта программа будет исполняться на конкретном компьютере формальную семантику Хаскелла не волнует.

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

все функции в сях чистые

Тогда прога сделает хлопок одной ладонью. Ну нету в сях никаких монадических слоёв.

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

Это разные программы на разных языках.

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

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

Ну нету в сях никаких монадических слоёв.

воображаемый монадический слой ))
ну смотри, допустим я пишу, на сях:
int x = GetX();
int y = GetY();
printf(«x + y = %d;\n», x+y);
Никаких сайдэффектов, пока не скомпилится в машкод.

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

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

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

воображаемый монадический слой ))

В принципе можно наверное и так, но к языку это не имеет отношения. На хаскеле тоже наверное можно писать как на фортране :)

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

data IO :: * -> * выше это фактически грамматика для императивного языка про установку и чтение времени, термы такого языка, вроде main, пишутся чистыми функциями (типа (>>=)), сами термы можно считать чистыми, в таком языке IO также возможно задействовать (погружать в монадический слой) чистые функции. Всё это может подвергаться частичным вычислениям (в пределе мы делаем нормальную форму main относительно SystemFC, но это не case, конечно). Такая интерпретация возможна, если мы согласны, что после main остаётся некий терм IO который нужно как-то «запустить», то есть продолжить семантику до победного. Разница в том, что тут это на уровне сигнатур, то есть чистота детектируется, возможные эффекты (относительно запуска терма IO) инкапсулированы (в терме IO).

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

ну смотри, допустим я пишу, на сях

Любая «дискуссия» по монадам переходит в терминальную стадию именно вот так. Пациент видит do-нотацию, от которой у него случается когнитивный диссонанс. И непонятно что лучше... То-ли в Applicative-стиле писать (\x y -> SetTime $ x-y) <$> GetTime <*> GetTime, то-ли с объяснением очевидного завязывать...

Monad в хаскеле, не для do-нотации. Вся фишка Monad, в волшебных пузырьках хитрожопом комбинаторе µ :: m (m a) → m a и наборе комбинаторов с типом a → m a, не являющимися return. Все вместе они задают логику работы «монады». Причем, через крайсивый и простой интерфейс: Functor + Monad + MonadPlus + Applicative + Alternative + некий MonadFoo — специфичный для данной монады тайпкласс, куда включены специфичные комбинаторы.

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

Т.е. в примере quasimoto GetTime, и SetTime не определяют каким именно образом будет производиться искомое действие. Вся логика определена в Monad, точнее, в неявном аналоге runIO :: m a → a — instance Show.

У тебя же — сплошной, махровый, императивный, сайд-эффектный ad-hoc. Со всеми вытекающими.

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

Это болтовня все. В литературе часто встречается противопоставление императивного декларативному, так и императивного функциональному, причем нередко функциональное чуть ли не отождествляют с декларативным (бывает и такое, кажется, у хаскелистов). Но то теория.

Теперь практика. Возьмем loop и iter из common lisp. По моему там иногда хрен разберешь, где заканчивается декларативное, а где начинается императивное.

В общем, как говорят наши англоговорящие заклятые друзья нелицемеры: «it depends on point of view».

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

Пациент видит do-нотацию

не вижу я ду-нотацию. :)
Я только вижу чистые функции и понимаю, что где-то они должны трансформироваться в нечистые для выполнения работы. Вот я и спрашиваю — где? Пока понял так, что это происходит где-то 'ниже уровнем', и поэтому считается что эта вся нечистота уже не на хаскеле, а 'на уровне' хаскеля всё чисто. А хаскель только строит последовательность вычислений.

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

Вот я и спрашиваю — где?

Знамо где. В рантайме, который совсем-совсем не чистый, и написан на C.

Если про Monad — то, как я уже писал, внутри µ. Только чистого µ как такового нет.

Наверное, основная причина непонимания работы Monad лежит в том, что когда начинающие смотрят на (>>=) :: m a → (a ­→ m b) ­→ m b, то думают что результирующий m b делает левая часть (f :: a → m b т.е.), а bind ee просто возвращает, но на самом деле, результирующий m b делает именно bind. Т.е. запускает некий runFoo :: m a → a для левой части, затем в зависимости от результата передает полученное значение в свою левую часть, еще раз запускает runFoo и оборачивает полученное значение в m через какой-то из комбинаторов a ­→ m b (не обязательно return). Если один из bind в цепочке не будет вычислять свою левую часть, то цепочка вычислений прервется.

Цепочка вычислений, она не совсем цепочка. Как и список, она напоминает луковицу (a >>= (\a' → b >>= (\b' → c >>= (\c' → ...)))), где bind сдирает очередной слой. Собственно, do-нотация и есть просто удобная запись вышеприведенного.

Можно придумать хитрый комбинатор, который будет противоположен bind, т.е. вычислять свою левую часть, когда bind ее не вычисляет. С помощью него можно организовать «обработку исключений».

Можно пойти дальше, и в качестве a использовать функцию... Что дает много интересных эффектов. Например, можно организовать несколько «точек выхода» http://www.scs.stanford.edu/11au-cs240h/notes/parsing-slides.html

PS: И да, бытует мнение что runIO не существует. Это мнение неправильное. Если он недоступен, как и конструктор IO, это не значит что его нет.

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

Сочетать в себе декларативность и необходимую для тюринг-полного практического ЯП конструктивность могло бы как раз ФП.

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