LINUX.ORG.RU

помогите выбрать ЯП


0

0

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

* сборщики информации
* действия
* диспетчер (получает информацию от сборщиков и вызывает соответствующие действие).

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

Питон отпадает ибо надоел, хаскел отпадает ибо я слишком туп для него. Вроде подходят CL и Erlang. Если взять коммон лисп, то можно будет очень удобно рулить системой с помощью Slime, эрланг такой штукой похвастаться не может, но зато философия языка очень соответствует моему плану.

Хотелось бы получить максимальное удовольствие от разработки, остальное все не так важно. Буду рад выслушать советы по выбору языка от уважаемых лоровцев. Опыт с ЦЛ у меня был лет 7 назад, на эрланге не программировал никогда.

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

Вот именно. Нам нужно остановить и перекомпилировать все приложение. Иначе - в хаскеле никак.

Зачем останавливаться и зачем всё приложение? Есть же граф вызовов, в твоём примере он такой:

f ----> g

Т.е. callers_of(f) = {g}, callers_of(g) = {}. Так что обновляя f можно обновить и g, но ничего больше не трогать. В общем случае, если нужно обновление n-ой глубины, front-end должен обновить f, обновить все callers_of(f), все callers_of_caller_of(f) и так далее n раз. Но это не всё приложение - только часть графа.

И все работает.

> (defun f () 1)
> (defun g (x) (+ (f) x))
> (g 1)
2
> (defun f () 10)
> (g 1)
11
> (defun f () "OH SHI~")
> (g 1)
debugger invoked on a SIMPLE-TYPE-ERROR in thread #<THREAD
                                                    "initial thread" RUNNING
                                                    {AB63721}>:
  Argument X is not a NUMBER: "OH SHI~"

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-KERNEL:TWO-ARG-+ "OH SHI~" 1)

В Haskell горячая замена не делается. Никак и никогда. По указанным выше причинам.

Она делается довольно легко, её не делают по другой причине - потому что подход к разработке несколько отличается от динамического.

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

Argument X is not a NUMBER: «OH SHI~»

Хотя, (declaim (ftype (function () integer) f)) спасает.

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

но вот только в этом случае сохранение типовых инвариантов тебе никто не гарантирует.

А что, где-то они гарантируются?

И ЯП надо тогда уже де-факто не статически типизирован

Это уже потрошки vm. Меня они, по большому счету, волновать не должны.

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

Есть же граф вызовов

А он есть? ;)

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

debugger invoked on a SIMPLE-TYPE-ERROR

Вот именно. Для динамического ЯП схватить ошибку типов в рантайме, корректно обработать ее и ехать дальше - это _штатная_ситуация. Тебе никто не гарантирует сохранение типовых инвариантов - просто потому, что их никто и не фиксирует. Статически типизированный же язык нам _гарантирует_ что никаких таких строк операция «+» не получит, она получает только числа. А тут мы суем в f строку, вызываем g и... что происходит в данный момент?

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

А что, где-то они гарантируются?

Ну статически типизированный ЯП от динамически типизированного именно тем и отличается - что гарантирует.

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

Она делается довольно легко

А можно пример, где она легко берет и делается? Вот так же как я накидал выше пример на лиспе.

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

Это уже потрошки vm.

Э? При чем тут vm? vm - это уже детали реализации, а нам как раз они не важны. Мы о самом языке говорим, а не о том, как он там реализован в потрохах vm.

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

Я тебя не понимаю. Есть функция, с какой-то сигнатурой. В ней бага. Мы правим багу. Конпелятор(!) нам говорит: «с точки зрения типов здесь всё хорошо, и в остальном приложении всё хорошо». Внимание вопрос. О каких проблемах с горячей заменой ты говоришь?

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

О каких проблемах с горячей заменой ты говоришь?

О тех, что либо компилятор нам врет, когда говорит, что «с точки зрения типов здесь всё хорошо, и в остальном приложении всё хорошо», либо он не даст нам сменить сигнатуру ф-и. Если первое - то нарушаются инварианты, если второе - то никакого хотпатчинга не выходит, так как почти всегда сигнатуры меняются.

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

так как почти всегда сигнатуры меняются.

Я и говорю, если так происходит, то это не горячяя замена, а содомия.

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

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

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

Я тебе уже отвечал про это. Еще раз, выбора два:

1. компилятор будет врать, когда говорит «с типами все хорошо» (то есть статическую типизацию можно засунуть в жопу).

2. менять сигнатуру будет нельзя (то есть никакой горячей замены нет, т.к. любое нетривиальное изменение ф-и потребует изменения сигнатуры)

а, ну еще можно делать ф-и Object -> Object. десятое правило Гринспуна во все поля.

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

1) Компилятор будет врать

Поделись упорином, а? Какая разница между запущенной с нуля системой, и заменой кода одной из функций (сигнатура не менялась), не учитывая исправленную ошибку?

2) *поменять* сигнатуру будет нельзя у функции, которая (в случае жабки) используется существующими инстансами).

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

и заменой кода одной из функций (сигнатура не менялась)

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

2) *поменять* сигнатуру будет нельзя у функции, которая (в случае жабки) используется существующими инстансами).

Ну так и что будет, когда нам в функцию, которая принимает число, придет строка?

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

Ты что хочешь мне доказать?

Статические языки на самом деле не статические, потому что в них возможна горячая замена?

Или:

В статических языках невозможна горячая замена?

Первое, очевидно ересь. Второе — есть жабка с HCR.

Ну так и что будет, когда нам в функцию, которая принимает число, придет строка?

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

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

А он есть? ;)

Он элементарно строится front-end-ом.

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

Как-то совсем не ясно - вот GHCi умеет замену одиночных определений, добавим граф вызовов, сохранение кода/AST определённых в сеансе функций и будем делать по несколько обновлений на раз (в соответствии с графом). Т.е. такого рода REPL это расширение GHCi-шного REPL-а.

Для динамического ЯП схватить ошибку типов в рантайме, корректно обработать ее и ехать дальше - это _штатная_ситуация.

«Штатная ситуация» когда приложение с тысячами кода попадает в состояние «Argument X is not an INTEGER: NIL» и выдаёт невменяемый трейс вызовов. При этом понятно, что ошибка-то элементарная - на месте аргумента который _должен_ иметь тип INTEGER оказывается NIL с типом NULL - простая система типов такое покрывает.

А тут мы суем в f строку, вызываем g и... что происходит в данный момент?

А мы так не будем делать, мы будем держать граф консистентным :)

А можно пример, где она легко берет и делается? Вот так же как я накидал выше пример на лиспе.

«Чтобы было как в лиспе» - я такого не видел. Вообще REPL-ы и разные пакеты для интерактивности (plugins, hint) довольно хилые. Но это проблема конкретной реализации, а не статической типизации вообще.

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

Статические языки на самом деле не статические, потому что в них возможна горячая замена?

В статических языках невозможна горячая замена?

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

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

Ага. То есть другими словами - как только мы сделали где-то горячую замену кода, то вся статическая типизация сразу идет по пизде и можно считать, что мы писали на динамике.

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

ЯП уже не может быть статически типизированным, т.к. не может гарантировать выполнение инвариантов.

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

и можно считать, что мы писали на динамике.

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

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

Он элементарно строится front-end-ом.

Чо, правда? А что будем делать с циклами?

Как-то совсем не ясно

Не, ну в сферической в вакууме ситуации все просто, офк, а вот как оно будет на практике...

«Штатная ситуация» когда приложение с тысячами кода попадает в состояние «Argument X is not an INTEGER: NIL» и выдаёт невменяемый трейс вызовов.

Любая ошибка, которая обрабатывается при помощи встроенных в ЯП методов обработки этих самых ошибок (в случае CL - рестарты, в случае других ЯП - исключения, например) - штатная ситуация. Собственно за тем эти рестарты/исключения и нужны, чтобы обрабатывать подобные ошибки штатным образом.

простая система типов такое покрывает

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

А мы так не будем делать, мы будем держать граф консистентным :)

То есть сигнатуру ф-и мы поменять не сможем (т.к. в этот момент граф станет неконсистентным), а значит никакой горячей замены у нас нет. Q. E. D.

Но это проблема конкретной реализации, а не статической типизации вообще.

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

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

От того что мы играемся с рантаймом

Какой рантайм? При чем тут рантайм? Мы говорили о языке, а не о рантайме. Если язык гарантирует выполнение инвариантов - он запрещает горячую замену (т.к. горячая замена эти инварианты нарушает).

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

Ну или не становится.

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

возможность взять все и перекомпилировать в соответствии с графом вызовов (если он таки есть) - это задача совершенно нетривиальная и это, по крайней мере, накладывает нехилые

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

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

Если язык гарантирует выполнение инвариантов - он запрещает горячую замену

Каким образом он это запретит? Тут защита такого же плана что и защита приватных членов.

Горячая замена делается не потому-что так можно, а потому что так нужно и удобно (иногда).

Отсюда рантайм. Без рантайма нет горячей замены, не так ли?

Ну или не становится.

Почему? Назови три причины.

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

Он элементарно строится front-end-ом.

Чо, правда? А что будем делать с циклами?

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

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

Не, ну в сферической в вакууме ситуации все просто, офк, а вот как оно будет на практике...

А на практике работает.

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

Чо, правда? А что будем делать с циклами?

В смысле с взаимной рекурсией? Это да, их нужно выявлять и для взаимно-рекурсивных функций делать обновление одновременно (также как, например, при определении в letrec).

То есть сигнатуру ф-и мы поменять не сможем (т.к. в этот момент граф станет неконсистентным), а значит никакой горячей замены у нас нет. Q. E. D.

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

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

Правда, всё это хорошо только до тех пор пока у нас чистые функции, а горячая замена чаще нужна для обновления I/O.

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

Но зачем вообще «штатно падать»? Это в теории можно обработать все такие «штатные ситуации», а на практике выходит, что в программе остаётся непокрытый control flow и программа падает совсем нехорошим образом в неожиданных местах.

Можно предположить, что в динамике мы достигаем покрытия по мере тестирования программы (т.е. упала - поправили, и всё крутится дальше), а в статике система типов заставляет нас обеспечить покрытие до запуска программы (тем большее, чем мощнее система типов). Но это не совсем так, просто потому что в динамике control flow более широкий, т.е. это вообще что угодно - функции могут принимать значения любого типа и есть бесконечное количество разных вариантов (все конструкторы всех типов), тогда как в статике control flow можно сильно ограничить типами и покрыть - покрытие по аргументу это покрытие всех конструкторов типа, это количество всегда конечно.

Не, ну в сферической в вакууме ситуации все просто, офк, а вот как оно будет на практике...

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

Ну вот примерный хелловорд, чтобы показать идею:

import Network
import System.IO
import Control.Exception
import Control.Concurrent

client :: HostName -> PortNumber -> IO ()
client host port = bracket (connectTo host $ PortNumber port) hClose send
  where
    send h = hGetLine h >>= putStrLn

server :: PortNumber -> IO ThreadId
server port = forkIO $ bracket (listenOn $ PortNumber port) sClose receive
  where
    receive s = accept s >>= handle >> receive s
    handle (h,_,_) = forkIO $ hPutStrLn h "hi!" >> hClose h
*Main> server 8080
ThreadId 26
*Main> client "localhost" 8080
hi!
*Main> client "localhost" 8080
hi!

Делаем в буфере:

client host port = bracket (connectTo host $ PortNumber port) hClose send
  where
-    send h = hGetLine h >>= putStrLn
+    send h = putStrLn "ready?" >> hGetLine h >>= putStrLn

делаем C-c C-l и уже:

*Main> client "localhost" 8080
ready?
hi!

т.е. server как работал в своих тредах, так и работает, а ghci тем временем обновляет модуль.

С другой стороны, чтобы вгорячую изменять уже сам сервер без его перезапуска - это я не знаю как сделать, можно на STM завязать и слать сообщения конфигурирования (на DSL, не на самом хаскеле), но получить доступ к этой STM из GHCi как-то не получается:

import Network
import System.IO
import Control.Exception
import Control.Concurrent
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar

client :: HostName -> PortNumber -> IO ()
client host port = bracket (connectTo host $ PortNumber port) hClose send
  where send h = hGetLine h >>= putStrLn

type Reply = TVar String

serverReply :: IO Reply
serverReply = newTVarIO "hi!"

server :: PortNumber -> IO ThreadId
server port = forkIO $ bracket (listenOn $ PortNumber port) sClose receive
  where receive s = accept s >>= handle >> receive s
        handle (h,_,_) = forkIO $ do
          var <- serverReply
          reply <- atomically $ readTVar var
          hPutStrLn h reply >> hClose h
*Main> server 8080
ThreadId 27
*Main> client "localhost" 8080
hi!
*Main> var <- serverReply
*Main> atomically $ writeTVar var "done?"
*Main> atomically $ readTVar var
"done?"
*Main> client "localhost" 8080
hi!

Хотя, может я что-то не так делаю.

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

это я не знаю как сделать

Если очень нужно, можно вынести инициализацию serverReply в main и делать её подгружая код инициализации из другого модуля с помощью GHC API (типа такого), но это почти тот же хак, что и сишные write to .c -> exex cc -> dlopen .so -> dlsym...

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

Хотя, может я что-то не так делаю.

import Network
import System.IO
import Control.Exception
import Control.Concurrent
import Control.Concurrent.STM
import Control.Concurrent.STM.TVar

client :: HostName -> PortNumber -> IO ()
client host port = bracket (connectTo host $ PortNumber port) hClose send
  where send h = hGetLine h >>= putStrLn

type Reply = TVar String

defaultServerReply :: IO Reply
defaultServerReply = newTVarIO "hi!"

server :: Reply -> PortNumber -> IO ThreadId
server config port = forkIO $ bracket (listenOn $ PortNumber port) sClose receive
  where receive s = accept s >>= handle >> receive s
        handle (h,_,_) = forkIO $ do
          reply <- atomically $ readTVar config
          hPutStrLn h reply >> hClose h
*Main> config <- defaultServerReply
*Main> atomically $ readTVar config
"hi!"
*Main> server config 8080
ThreadId 29
*Main> client "localhost" 8080
hi!
*Main> atomically $ writeTVar config "done?"
*Main> atomically $ readTVar config
"done?"
*Main> client "localhost" 8080
done?

Т.е. всё довольно просто. Естесвенно, в STM можно хранить как данные так и код (функции), так что server оказывается полнгостью обновляемым - это вообще может быть заглушка вытаскивающая весь горячий код из STM.

quasimoto ★★★★
()

жованый крот, запретите уже такие топики, всем же ясно, во что они скатываются каждый раз

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

Механизмы разные есть. Ты вообще что сказать-то хотел?

То, что я хотел сказать, я сказал.

В моем описании конкретный механизм замены кода не раскрывается, это ровно описание того, что такое горячая замена кода.

Поделись уже сакральным знанием о горячей замене кода в хацкеле. Либо уж GTFO.

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

А на практике работает.

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

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

В смысле с взаимной рекурсией?

Нет. f x = x, g x = f g. Рекурсии тут нет, понятное дело, а цикл в графе вызовов - есть.

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

Если сигнатура поменялась - то граф неконсистентен, все.

Но зачем вообще «штатно падать»?

А зачем вообще нужны исключения?

а на практике выходит, что в программе остаётся непокрытый control flow и программа падает совсем нехорошим образом в неожиданных местах.

На практике как раз такого почти никогда не бывает. Кстати, не забываем про soft typing (когда в динамическом ЯП просто выдаются варнинги, если компилятор доказывает некорректность). Простой типизированной лямбды с объединениями и подтипированием вполне достаточно чтобы вывести типы для 99% лиспокода и указать практически все места, где «2» + 2.

Можно предположить блаблабла

Это все голая теория, практика показывает, что ошибки вида «2» + 2 выявляются при первом же тестовом запуске ф-и - причем _отдельного_ теста для выявления таких ошибок не надо, ошибка будет выявлена при запуске теста на логику (который любом случае придется выполнить - что в случае динамики, что в случае статики). Так что ошибка находится точно в тот же момент, что и в случае тайпчека. Только благодаря тому, что это рантайм - сообщение об ошибке гораздо более информативно, что позволяет ее сразу исправить, а не думать, что значит это невнятно сообщение о «no instance for», а потом искать - где же того инстанса нет (а не быть его может где угодно, потому что тайпинференс).

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

Какая разница? Проблема-то с неконсистентностью графа все равно остается. Не говоря о том, что придется делать реификацию для high-rank/kind полиморфизма.

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

nuff said. То есть горячей замены нет. И смысл было писать все эти стены текста? То, что можно переписать код, а потом его скомпилировать - это никто не сомневался. Для этого достаточно иметь компилятор.

можно на STM завязать

А это уже из разряда «знал бы где падать - соломку бы подстелил». Мы же заранее не можем предсказать что нам надо будет в STM оборачивать.

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

defaultServerReply = newTVarIO «hi!»

А теперь сделай, чтобы было defaultServerReply = «hi!»

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

Это все голая теория, практика показывает, что ошибки вида «2» + 2 выявляются при первом же тестовом запуске ф-и - причем _отдельного_ теста для выявления таких ошибок не надо, ошибка будет выявлена при запуске теста на логику (который любом случае придется выполнить - что в случае динамики, что в случае статики).

Исправлять ошибки во время написания кода гораздо удобнее и быстрее, чем при тестировании. Тесты на логику, кстати, в подавляющем случаев проходят без ловли багов, отсюда и миф про «скомпилировалось — значит работает».

Только благодаря тому, что это рантайм - сообщение об ошибке гораздо более информативно, что позволяет ее сразу исправить, а не думать, что значит это невнятно сообщение о «no instance for», а потом искать - где же того инстанса нет (а не быть его может где угодно, потому что тайпинференс).

Это тебя занесло уже конкретно, конечно. «No $class instance for $type» гораздо информативнее «The value $value is not of type $type».

P.S. Горячей замены, конечно же, нет. Но, надо сказать, не так уж она и необходима. Как минимум, она вызывает трудности с перевычислением тех объектов, которые родились с помощью старого кода. При этом еще и ввод/вывод учитывать надо. В общем, побочные эффекты от нее труднопредсказуемы. Так еще и необходимость применения этой техники крайне, крайне редка.

anonymous
()

Зачем тебе горячая замена?

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

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

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

Тесты на логику, кстати, в подавляющем случаев проходят без ловли багов, отсюда и миф про «скомпилировалось — значит работает».

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

Это тебя занесло уже конкретно, конечно. «No $class instance for $type» гораздо информативнее «The value $value is not of type $type».

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

P.S. кококо нинужна кококо

нутыпонел

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

С тобой невозможно спорить, пишешь еще большую чушь, чем я.

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

Нет. f x = x, g x = f g. Рекурсии тут нет, понятное дело, а цикл в графе вызовов - есть.

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

Если сигнатура поменялась - то граф неконсистентен, все.

Пример workflow в хаскеле который даст неконсистентный граф (т.е. пропустит плохо-типизированный код в рантайм)?

А зачем вообще нужны исключения?

Чтобы платить за возможность иметь частичные функции, при только алгебраических типах и только тотальных функциях исключения остались бы разве что внутри I/O и это были бы даже не совсем те исключения, а просто какие-то специальные комбинаторы.

На практике как раз такого почти никогда не бывает.

Что правда? Хорошо если так :)

Какая разница? Проблема-то с неконсистентностью графа все равно остается. Не говоря о том, что придется делать реификацию для high-rank/kind полиморфизма.

Как раз тут уже всё хорошо - можно как угодно редактировать дерево проекта, потом сделать C-c C-l в одном модуле и тогда и модуль и его зависимости, если они изменились, будут обновлены. Это как раздельная компиляция только с REPL-ом. Всё останется консистентным и всё измененное будет обновлено, при этом уже работающий I/O код (в других тредах) продолжит работать.

То есть горячей замены нет.

Я же привёл пример с заменой кода сервера в STM.

А это уже из разряда «знал бы где падать - соломку бы подстелил».

Видимо это разного рода обновление - в лиспе оно инструмент отладки, а в хаскеле, где такая отладка *не нужна* потому что есть система типов, это инструмент конфигурирования приложения без его остановки.

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

Пример workflow в хаскеле который даст неконсистентный граф (т.е. пропустит плохо-типизированный код в рантайм)?

Извини, но ты тупой? Еще раз - если сигнатура поменяется, то граф обязательно будет неконсистентным. Если граф консистентен - значичт сигнатура _не_ менялась. По-этому делать хот-патчинг со сменой сигнатуры _нельзя_. И примеров по этой причине - нет. Нельзя привести пример с плохо типизированной сменой сигнатуры, потому что вообще нельзя привести пример со сменой сигнатуры. Понятно так?

Чтобы платить за возможность иметь частичные функции, при только алгебраических типах и только тотальных функциях исключения остались бы разве что внутри I/O и это были бы даже не совсем те исключения, а просто какие-то специальные комбинаторы.

Нет, я серьезно спрашивал. То есть зачем исключения _в реальности_ (в тех языках, что используются на практике, например), а не в мокрых фантазиях хаскишкольников.

Что правда? Хорошо если так :)

Да, правда. Я тоже этому рад - можно просто писать программу, не тратя кучу времени на бесполезную борьбу с системой типов :)

Я же привёл пример с заменой кода сервера в STM.

Ты сделал ровно вот что:

> (define x 1)
> (define (g) x)
> (define (set-x n) (set! x n))
> (g)
1
> (set-x 2)
> (g)
2
> 
всем совершенно очевидно, что это не имеет никакого отношения к горячей замене.

Видимо это разного рода обновление - в лиспе оно инструмент отладки, а в хаскеле, где такая отладка *не нужна* потому что есть система типов, это инструмент конфигурирования приложения без его остановки.

Ну это же чушь и неправда. С точки зрения отладки, как мы уже выше выяснили, лисп от хаскеля ничем не отличается. А горячая замена - это _не_ инструмент отладки. Это инструмент патчинга, он применяется уже после релиза, когда все отлажено. И он либо фиксит баги, которые были пропущены во время отладки (а значит системой типов не ловятся, и в хаскеле так же вылезут в рантайме), либо вообще не ошибки исправляет, а, внезапно, меняет функционал в соответствии с изменившимися спецификациями.

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

Еще раз - если сигнатура поменяется, то граф обязательно будет неконсистентным.

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

То есть зачем исключения _в реальности_

Для всяких «connection closed by peer» и т.п. _реальных_ I/O ситуаций. А когда они суются в частичные функции потому что у на строки на место чисел попадают - это печально (в динамике с этим можно мириться под тем предлогом, что никаких тотальных функций для аргументов типа t (a.k.a. Dynamic) вообще быть не может).

Ты сделал ровно вот что

Я показал вот что - функция g в своём определении использует некую f. Вопрос - как запустить g так, чтобы при изменении функции f функция g тоже изменила своё поведение? Короче, чтобы было как в лиспе. Если f ссылается на один код в памяти, и мы делаем (set! f new-code), то в памяти появляется другой код к которому теперь ведёт символ 'f, а старый код висит дожидаясь GC. Это и есть горячая замена f, которая меняет поведение также всех тех функций которые вызывают f. В хаскеле всё это делается передачей функции g через аргумент STM в которой может лежать и f и всё остальное.

А горячая замена - это _не_ инструмент отладки. Это инструмент патчинга, он применяется уже после релиза, когда все отлажено.

Т.е. чтобы ползать по коду приложения и вживую его переписывать, да? Вместо того чтобы раздельно компилировать/запускать/компилировать/запускать.

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

Т.е. чтобы ползать по коду приложения и вживую его переписывать, да? Вместо того чтобы раздельно компилировать/запускать/компилировать/запускать.

http://www.infoq.com/interviews/armstrong-peyton-jones-erlang-haskell - вспомнилось.

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

Нет, я серьезно спрашивал. То есть зачем исключения _в реальности_ (в тех языках, что используются на практике, например), а не в мокрых фантазиях хаскишкольников.

Лиспошкольники не знают, зачем нужны исключения?

Я тоже этому рад - можно просто писать программу, не тратя кучу времени на бесполезную борьбу с системой типов :)

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

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

Если ты изменишь сигнатуру некоторой функции f, то ты по сути введёшь новую версию f, все функции что использовали f до этого так и будут использовать старую версию со старой сигнатурой

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

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

Почему?

А когда они суются в частичные функции потому что у на строки на место чисел попадают - это печально

Когда они суются в частичные функции потому что на место ненулевых чисел попадает 0 - это не менее печально. Даже более - когда выкидывание этих исключений противоречит всей идеологии языка. Но лучше же замести проблему под ковер при помощи _|_ и сделать хорошую мину при плохой игре.

Я показал вот что - функция g в своём определении использует некую f. Вопрос - как запустить g так, чтобы при изменении функции f функция g тоже изменила своё поведение?

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

Т.е. чтобы ползать по коду приложения и вживую его переписывать, да? Вместо того чтобы раздельно компилировать/запускать/компилировать/запускать.

О чем ты? Какое ползание по коду и переписывание вживую? Переписывается все в точности, как и на каком-нибудь хаскеле. Только потом, когда уже переписали, вместо «остановить приложение, скомпилировать новую версию, заменить, запустить» мы просто вносим изменение в работающий образ.

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

Лиспошкольники не знают, зачем нужны исключения?

Мне лишь хотелось узнать, знают ли об этом хаскишкольники.

Лисперы предпочитают узнавать о багах как можно позже?

Зачем же позже? Наоборот - раньше. Пока хаскидебил чтото компилирует, парсит ошибку чекера и пытается понять - о чем это вообще, я уже запустил тест, нашел эту ошибку и исправил ее. Это уже не говоря о том, что чекер статического ЯП работает фундаментально неверно - вместо того, чтобы отвергать код _некорректность_ которого доказана, он отвергает код, для которого не может доказать _корректность_. В результате для правильной программы чекер часто выдает ошибку (которой бы в динамике вообще не было, ведь программа - правильная) и нам приходится писать аннотации/переписывать программу, чтобы чекер смог таки доказать корректность. Еще раз - переписывать _правильную_ программу, чтобы тупой чекер ее пропустил. Ну не дебилизм ли? Какой вообще адекватный человек будет тратить время на такую хуйню?

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

Мне лишь хотелось узнать, знают ли об этом хаскишкольники.

Гнилые отмазки фанбоя.

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

Смирись, ты просто не способен пользоваться нормальными инструментами. Обычный хацкеллист в течении доли секунды понимает, где ошибка и как ее исправить. И он это сделает в 100 раз быстрее лиспозадрота, который будет на макроэкспанды медитировать.

Это уже не говоря о том, что чекер статического ЯП работает фундаментально неверно - вместо того, чтобы отвергать код _некорректность_ которого доказана, он отвергает код, для которого не может доказать _корректность_.

Уж лисподебилоид-то точно знает, как должен тайпчекер в статике работать!

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

Еще раз - переписывать _правильную_ программу, чтобы тупой чекер ее пропустил.

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

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

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

Практика доказывает, что это не так.

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

Ну да, конечно. Достаточно на первую страницу fprog.ru глянуть, чтобы убедиться, что это не так.

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

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

Работает = «выдает ошибку типов»? Ну тогда да, работает.

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