![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Читаю книжку Одерского, до основной шизы еще не добрался, но такое ощущение, что в скале чрезмерно много синтаксического сахара. Типа "тут вы можете скобки опустить, а тут вместо скобок использовать фигурные скобки, а тут мы прямо в параметрах класса сделаем их полями, а в multiline string literal вы можете сделать отступ и stripMargin" и прочая и прочая в том же духе.
Основное из этого, видимо - function literals и вызов методов в стиле a methodName b, без точек и скобок, что делает код более лаконичным, одновременно позволяя при желании превратить код в нечитабельный ад.
Заодно по наводке
jdevelop глянул на http://spray.io/ https://github.com/spray/spray/wiki
Примеры там, конечно, знатный abuse возможностей языка и вычислений на типах, типа extraction-директив с HList в качестве параметра типа.
Clojure по сравнению с этим выглядит более простой и логичной, хотя я не уверен, можно ли сравнивать совершенно разные языки, общего у которых только функциональщина и иммутабельность иногда.
PS: Вот, к примеру:
https://github.com/spray/spray/blob/master/docs/documentation/spray-routing/code/docs/HttpServiceExamplesSpec.scala
В SimpleService HttpResponse реализован как html-код написанный прямо внутри скала-кода.Сижу уже 30 минут ищу, где это преобразование реализовано и как. Т.е. не видя отдельных литералов и их типов (которые без загрузки всего оного кода с зависимостями в IDE/интерпретатор еще и не увидишь), с ходу догадаться, что происходит, достаточно сложно. XML literals, встроенные в язык и где-то implicit для конверсии.
PPS: implicit evidence:
http://jim-mcbeath.blogspot.com/2008/11/scala-type-infix-operators.html
http://stackoverflow.com/questions/3427345/what-do-and-mean-in-scala-2-8-and-where-are-they-documented
По-моему, это уже достаточно сложно, чтобы увлечь психов и стать новыми крестиками. Вот
xeno_by еще приделает макросы - и совсем хорошо станет.
Основное из этого, видимо - function literals и вызов методов в стиле a methodName b, без точек и скобок, что делает код более лаконичным, одновременно позволяя при желании превратить код в нечитабельный ад.
Заодно по наводке
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
Примеры там, конечно, знатный abuse возможностей языка и вычислений на типах, типа extraction-директив с HList в качестве параметра типа.
Clojure по сравнению с этим выглядит более простой и логичной, хотя я не уверен, можно ли сравнивать совершенно разные языки, общего у которых только функциональщина и иммутабельность иногда.
PS: Вот, к примеру:
https://github.com/spray/spray/blob/master/docs/documentation/spray-routing/code/docs/HttpServiceExamplesSpec.scala
В SimpleService HttpResponse реализован как html-код написанный прямо внутри скала-кода.
PPS: implicit evidence:
http://jim-mcbeath.blogspot.com/2008/11/scala-type-infix-operators.html
http://stackoverflow.com/questions/3427345/what-do-and-mean-in-scala-2-8-and-where-are-they-documented
По-моему, это уже достаточно сложно, чтобы увлечь психов и стать новыми крестиками. Вот
![[livejournal.com profile]](https://www.dreamwidth.org/img/external/lj-userinfo.gif)
no subject
Date: 2012-11-09 04:20 am (UTC)Такой if в любом языке пишется, достаточно будет обернуть аргументы в delay. С другой стороны, в хаскеле нельзя реализовать свой begin - он в хаскеле встроен (и называется case). То есть в хаскеле приходится руками оборачивать выражения в force. Это известная эквивалентность ленивого и энергичного порядка. На языке теорката она выражается в том, что в категории соответствующей ленивому порядку нету сумм, а в категории, соответствующей энергичному порядку - произведений, т.о. полученные категории дуальны.
no subject
Date: 2012-11-09 09:25 am (UTC)В строгом языке - все аргументы, в ленивом - в нескольких местах.
no subject
Date: 2012-11-10 09:37 am (UTC)no subject
Date: 2012-11-10 10:45 am (UTC)no subject
Date: 2012-11-11 12:57 am (UTC)В ленивом языке надо всегда ставить force вокруг первого аргумента кейза, В энергичном - delay вокруг веток if. Но поскольку и там и там это делает невыразимая в рамках языка спецформа (case и if соответственно)., то на практике ни там ни там никто вообще ничего не ставит. Важно, что ситуация совершенно зеркальна.
Тут следует только отметить, что force в ленивом ЯП выразить нельзя (если у нас уже не встроен кейз, который неявно вызывает force как в хаскеле), А вот delay выразить можно - это санка. Естественно, если мы говорим о классическом лямбда-исчислении, то там будет нарушена семантика (т.к. санка необходимо должна иметь переменную), так что указанная выше дуальность не нарушена. Но с точки зрения "человеческой" семантики функция без аргументов, которая моделирует delay в энергичном языке выглядит намного естественнее и лучше вписывается в исчисление, чем force, который вообще неясно как выражать, чистый хак.
no subject
Date: 2012-11-11 01:23 am (UTC)fibs = 0:1:zipWith (+) fibs (tail fibs)
Я пишу всего две функции, которые будут считать строго голову и строго хвост и сделаю список чисел Фибоначчи полностью строгим.
http://thesz.livejournal.com/906786.html
fibsAsBottom = strictTail $ strictHead $ fibs
Сколько и чего вам нужно будет написать, чтобы сделать из бесконечного энергичного списка чисел Фибоначчи ленивый? Считайте, что все функции в первом определении выше энергичные, и сам список тоже.
no subject
Date: 2012-11-11 03:10 am (UTC)Я не понял вопроса. Что из чего следует сделать? Известно, что "энергичные бесконечные списки" не существуют. Как из того, что не существует, можно сделать нечто существующее?
Сформулируйте четко и ясно, что вы хотели сказать, пользуясь общепринятой терминологией и не выдумывая своих понятий. Это необходимое условие, чтобы собеседник мог вас понимать.
Напоминаю, о чем мы говорили - на каждый if/case в энергичном/ленивом языке надо ставить delay/force, иначе ничего не будет работать. Т.о. места обязательных расстановок lazy/strict аннотаций строго совпадают. При чем эти аннотации расставляются автоматически, соответствующей спец-формой (if/case). В результате в коде никаких аннотаций нет. Ни в ленивом ни в энергичном случае. Это подтверждается экспериментально - мы спокойно пишем код безо всяких аннотаций. Потому в большинстве языков даже и возможности расставить аннотации руками нет - это просто никому не нужно.
Теперь, еще раз, что вы хотите доказать и как это соотносится с числами Фибоначчи?
no subject
Date: 2012-11-11 10:04 am (UTC)no subject
Date: 2012-11-11 03:31 am (UTC)> По моим соображениям, при энергичном порядке вычислений по умолчанию и ленивости в виде аннотаций, эти самые аннотации надо расставлять везде, где по дизайну программы могут проходить ленивые значения и их надо сохранить ленивыми. Это практически все библиотечные функции, по-моему. А также очень много мест в коде программы - еще раз подчеркну: везде, где по дизайну программы могут проходить ленивые значения и их надо сохранить ленивыми.
Как я понимаю вопрос лишь в том, что считать умолчанием, а что выставлять аннотациями. На мой взгляд все очевидно - аннотациями должна выставляться ленивость. Т.к., в самом деле, если для вычисления с точки зрения семантики без разницы, быть ленивым или энергичным, то его следует делать энергичным из соображений производительности. Поскольку к таким выражениям относятся 99% выражений во всей программе - если предположить иное, то получилось бы, что нам практически на каждое выражение надо ставить аннотацию строгости.
Ваш аргумент можно было бы принять, но вы подразумеваете тут полную бесконтрольность - то есть у нас-де ВНЕЗАПНО может куда-нибудь прийти санка, а раз может...
Но такой ситуации быть не должно, конечно же - все места, куда должно прийти ленивое вычисление, должны быть фиксированы заданием соответствующего типа. f: Promise Int -> Int - может прийти санка, f : Int -> Int - не может прийти санки. Делаем Promise монадой и обеспечиваем автоматический лифтинг ф-й в нее - в результате весь код будет выглядеть одинаково - и ленивый и энергичный, а нам лишь надо будет в граничных местах заворачивать или вытаскивать значение из lazy-монады где это явно надо.
То есть если лень неконтролируема (как в хаскеле) - автоматическая ленивость с опциональной энергичностью имеет смысл, но если есть контроль (как это должно быть) - смысла нет, автоматическая лень вредит и все усложняет.
no subject
Date: 2012-11-11 10:18 am (UTC)Для меня важней производительность программиста. Поэтому для меня предпочтительней ленивый порядок вычислений, при котором выше модульность программы и её проще писать, ибо не надо задумываться, что же будет вычислено, а что нет.
no subject
Date: 2012-11-12 05:47 am (UTC)В общем, описанное выше решение (фиксация порядка вычислений типами) как раз и нужна за тем, чтобы не задумываться - то есть если нам хочется узнать, что и как вычисляется, то нам в этом случае не надо исследовать control-flow, достаточно посмотреть на тип выражения.
Еще можно вспомнить о том, что бОльшая часть юзкейзов лени покрывается ленивыми структурами. В этом смысле какой-нибудь map для списка не должен писаться руками - он должен быть сгенерирован автоматически, исходя из определения АТД "List" и генерализованного определения map для произвольного АТД (как, например, это можно делать в Clean). Для ленивого списка мы автоматически получим ленивый map, для энергичного списка - энергичный map, а переход между способами вычисления будет выглядеть как явное преобразование типа соответствующей структуры.
no subject
Date: 2012-11-12 06:27 am (UTC)no subject
Date: 2012-11-12 08:55 am (UTC)no subject
Date: 2012-11-12 09:00 am (UTC)Проблем с оптимизацией ленивой программы не более, чем проблем с оптимизацией программы на C под Cell BE. Со сложностью то же самое.
Мой подход состоит в экономии времени программиста, чтобы тот совершил больше полезных для понимания предметной области ошибок, а не оптимизировал ещё не написанную программу.
no subject
Date: 2012-11-12 11:46 am (UTC)Начнем сызнова - ваш тезис, как я понимаю, состоит в следующем: ленивый порядок экономит время программиста, поскольку делать выводы о ходе вычисления проще при ленивом порядке.
Здесь есть посылка (делать вывод о ходе вычисления проще при ленивом порядке) и следствие (программист экономит время).
Чтобы следствие получилось верным, надо показать, что верна посылка. Но тут-то и возникает проблема - есть известные факты, которые посылку опровергают. То есть программист, может, время и экономит - но явно не потому, что в ленивом порядке делать выводы о ходе программы проще - т.к. известно, что делать эти выводы при ленивом порядке _сложнее_.
no subject
Date: 2012-11-12 07:25 pm (UTC)Вот тут дилемма.
Стоит ли нашему читателю верить Валентину Будаеву или Джону Хьюджесу и Леннарту Аугустссону?
Вадлер, кстати, утверждает дуальность call-by-name и call-by-value. Поэтому на статью с показанной дуальностью call-by-value и call-by-need я бы хотел посмотреть.
И ещё хочу попросить у вас ссылку на статью, которая показывает повышение модульности программ на энергичном языке по сравнению с ленивым. WhyFP Джона Хьюджеса показывает повышение модульности программ только для ленивых языков, надо исправить это упущение.
no subject
Date: 2012-11-13 02:23 am (UTC)А зачем кому-то верить? Я предпочитаю смотреть на экспериментальные факты, а не на чьи-то мнения и статьи (которые тоже суть не более чем мнения). А экспериментальные факты таковы, что при оценке сложности и оптимизации программ ленивый порядок не упрощает, а усложняет reasoning.
> И ещё хочу попросить у вас ссылку на статью, которая показывает повышение модульности программ на энергичном языке по сравнению с ленивым. WhyFP Джона Хьюджеса показывает повышение модульности программ только для ленивых языков, надо исправить это упущение.
Во-первых, это статья о том, как FP поднимает модульность в языках вроде хаскеля. К другим ЯП сделанные в статье выводы, конечно же, неприменимы. Во-вторых, статья рекламная - а энергичному порядку реклама не требуется, Это лень на данный момент маргинальна.В-третьих, я могу ошибаться (вы меня тогда поправьте), но вот тот же Саймон Пейтон-Джонс как-то высказывался на тему того, что лучше было бы хаскелю быть энергичным, нет?
> Поэтому на статью с показанной дуальностью call-by-value и call-by-need я бы хотел посмотреть.
call-by-need и call-by-name изоморфны в обсуждаемом смысле.
no subject
Date: 2012-11-13 04:19 am (UTC)Неверно. Рассуждения о программе усложняются, если пытаться использовать императивный способ рассуждений, основанный на понятии "ход вычислений".
Если же использовать семантику Скотта, то рассуждения становятся проще в разы (а то и в десятки раз).
no subject
Date: 2012-11-14 03:46 am (UTC)От того, что вы белое назовете черным, оно не станет таковым. Проблемы с оценкой сложности ленивых программ - известный факт.
> Если же использовать семантику Скотта, то рассуждения становятся проще в разы (а то и в десятки раз).
Было бы неплохо продемонстрировать этот тезис на каком-нибудь простом примере. Ну, например, докажите в семантике Скотта ассоциативность ф-и append.
no subject
Date: 2012-11-14 08:21 am (UTC)Фу, как некрасиво. Только что говорили вообще про рассуждения о программе — и вдруг спрыгиваете на оценку сложности.
> Ну, например, докажите в семантике Скотта ассоциативность ф-и append.
Это которая в Хаскеле (++)? Конкатенация списков?
Пожалуйста.
Определение:
append [] ys = ys
append (x:xs) ys = x : append xs ys
Требуется доказать:
append xs (append ys zs) = append (append xs ys) zs
Расшифровка определения: если положить
t f [] ys = ys
t f (x:xs) ys = x : f xs ys
то append — наименьшая неподвижная точка функции t, то есть, наименьшая f, для которой t f = f.
Далее, если f такова, что f xs (append ys zs) = append (f xs ys) zs, то t f тоже удовлетворяет этому условию, так как
t f [] (append ys zs) = append ys zs = append (t f xs yz) zs
t f (x:xs) (append ys zs) = x : f xs (append ys zs) = x : append (f xs ys) zs = append (x : f xs ys) zs = append (t f (x:xs) ys) zs
Функция (_|_) этому условию удовлетворяет:
(_|_) xs (append ys zs) = (_|_) = append (_|_) zs = append ((_|_) xs ys) zs
Значит, условие удовлетворяется для tn(_|_) при любом натуральном n. В силу непрерывности обеих его частей по f, оно удовлетворяется также и для supntn(_|_) — а это и есть append.
(no subject)
From: (Anonymous) - Date: 2012-11-15 04:46 am (UTC) - Expand(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-11-13 10:20 am (UTC)Действительно, СПЖ сказал в интервью, что чистота важнее порядка выполнения и что "следующий Хаскель" может быть и энергичным. Просто проще всего сделать язык чистым с помощью ленивого порядка выполнения.
Который, к тому же, ещё и удобней для программирования.
Насчёт маргинальности... По-моему, умного человека смущает не количество единомышленников, а их качество. Мне нравится Haskell community.
Прошу прощения, но упомянутые вами "эксперименты" пока выглядят всего лишь как ещё одно мнение. Эксперименты должны иметь результаты в виде цифр. Вот таковые про Хаскель и энергичные языки.
И я хочу увидеть чьё-то ещё мнение насчёт изоморфизма call-by-value и call-by-need. Только вашего мне недостаточно. Вы рассматриваете этот пока ещё не существующий для меня дуализм на слишком низком уровне.
no subject
Date: 2012-11-14 03:49 am (UTC)> Вот таковые про Хаскель и энергичные языки.
Вы сами догадаетесь, куда следует засунуть статистические эксперименты без обоснования репрезентативности выборки, или вам подсказать?
(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:(no subject)
From:no subject
Date: 2012-11-11 03:38 am (UTC)Фактически, вы сделали следующее:
map = tmp
теперь если я хочу сделать map энергичным, то просто переопределяю tmp, а реализацию map менять не надо!
Ну так это работает и с энергичным и с ленивым порядком :)
no subject
Date: 2012-11-11 01:29 am (UTC)Ленивое ЛИ Тьринг полно. Значит, в нем можно реализовать force.
В приведенных мной примерах это и делается.
no subject
Date: 2012-11-11 03:01 am (UTC)Энергичное тоже :)
> Значит, в нем можно реализовать force.
Нет, нельзя. условно говоря, delay/force нельзя реализовать в энергичном/ленивом ЯП. С тьюринг-полнотой это не связано - тьюринг-полнота о вычислимости, а не о выразительности.
С точки зрения тьюринг-полноты - ну вы if, напишите, а вот сделать так, чтобы он работал как нормальный if (то есть на булевых значениях) способа никакого нет. Хотя это вобщем-то не важно.