metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2012-11-06 04:15 am
Entry tags:

Scala

Читаю книжку Одерского, до основной шизы еще не добрался, но такое ощущение, что в скале чрезмерно много синтаксического сахара. Типа "тут вы можете скобки опустить, а тут вместо скобок использовать фигурные скобки, а тут мы прямо в параметрах класса сделаем их полями, а в multiline string literal вы можете сделать отступ и stripMargin" и прочая и прочая в том же духе.
Основное из этого, видимо - function literals и вызов методов в стиле a methodName b, без точек и скобок, что делает код более лаконичным, одновременно позволяя при желании превратить код в нечитабельный ад.

Заодно по наводке [livejournal.com profile] 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

По-моему, это уже достаточно сложно, чтобы увлечь психов и стать новыми крестиками. Вот [livejournal.com profile] xeno_by еще приделает макросы - и совсем хорошо станет.

[identity profile] thesz.livejournal.com 2012-11-07 10:32 am (UTC)(link)
Вы определение && на Хаскеле видели?

И как я понимаю, булевский тип тоже встроен.

[identity profile] isorecursive.livejournal.com 2012-11-07 10:45 am (UTC)(link)
Что-нибудь такое ?:
True && True = True
_    && _    = False

Дело в том, что паттерн-матчинг в хаскеле дешугарится в case, а это такой же встроенный примитив, как и if.

@ И как я понимаю, булевский тип тоже встроен.
Ну это всё мелочи, по-моему. Можно вот так:
http://www.scalakata.com/509a3b5ce4b093f3524f38fd
и так:
http://www.scalakata.com/509a3bb3e4b093f3524f38ff
Edited 2012-11-07 10:46 (UTC)

[identity profile] thesz.livejournal.com 2012-11-07 11:22 am (UTC)(link)
Нет, не такое. Ваш оператор не будет short circuit. В частности, для False && error "Badam!" будет вычислен error "Badam!"

True && x = x
False && _ = False

Видите ли, в чём дело. Я могу 1) сделать свой собственный if и 2) воспользоваться кодированием конструкторов и деструкторов (сравнение с образцом) через функции. Это так называемое кодирование Чёрча.

Если вы не знали, то вот вам пример (я уж задолбался его приводить):
*Main> let true = \a b -> a
*Main> let false = \a b -> b
*Main> let a &&& b = a b false
*Main> let ifThenElse c a b = c a b
*Main> ifThenElse (true &&& false) 10 12
12
*Main> ifThenElse (true &&& true) 10 12
10
*Main> ifThenElse (true &&& true) 10 (error "Badam!")
10
*Main> ifThenElse (false &&& error "Badam!") 10 12
12

Попробуйте сделать такое на Scala.

На таком кодировании основана AwesomePrelude - как можно взять практически обычный Хаскель (пара расширений) и скомпилировать его в JS библиотекой. Без макросов. Без трансляции, как у меня в HHDL. Простой библиотекой.

Short-circuit операторы я могу также сделать и для других значений. Например, для Q Bool, если сочту это нужным. Поэтому я и обратил ваше внимание на встроенность bool.

[identity profile] xeno-by.livejournal.com 2012-11-07 12:36 pm (UTC)(link)
Касательно AwesomePrelude предлагаю посмотреть в сторону http://infoscience.epfl.ch/record/179888/files/js-scala-ecoop.pdf. Тоже делается Скалой с парой расширений.

[identity profile] thesz.livejournal.com 2012-11-07 01:54 pm (UTC)(link)
Интересно.

Получение AST из выражений я и сам хотел.

[identity profile] xeno-by.livejournal.com 2012-11-07 12:38 pm (UTC)(link)
> для False &&& error "Badam!" будет вычислен error "Badam!"
Не будет, т.к. соответствующий параметр call-by-name.

[identity profile] isorecursive.livejournal.com 2012-11-07 01:05 pm (UTC)(link)
@ Нет, не такое. Ваш оператор не будет short circuit. В частности, для False && error "Badam!" будет вычислен error "Badam!"
Не будет: http://ideone.com/1Tcr6D

@ Попробуйте сделать такое на Scala.
Не проблема: http://www.scalakata.com/509a5a9fe4b093f3524f3994

В скале нотация "(=> T)" в аргументах обозначает by-name.

[identity profile] thesz.livejournal.com 2012-11-07 01:53 pm (UTC)(link)
Согласен, прикольно. Чем-то Алгол-68 напоминает.

Как я понимаю, call-by-name аргумент будет вычислен столько раз, сколько мы его будем использовать?

[identity profile] metaclass.livejournal.com 2012-11-07 02:12 pm (UTC)(link)
Да, он не мемоизируется, да и мутабельность там везде, много не намемоизируешь.

[identity profile] thesz.livejournal.com 2012-11-07 02:29 pm (UTC)(link)
Тогда я останусь при моём Хаскеле без макросов.

[identity profile] isorecursive.livejournal.com 2012-11-07 03:38 pm (UTC)(link)
@ Алгол-68
Почему-то рассмешило.

@ Как я понимаю, call-by-name аргумент будет вычислен столько раз, сколько мы его будем использовать?
Ну да, потому и by-name.
Кстати, только что придумал как на implicit-конверсиях сделать by-need!
Конечно, оверхед по производительности побольше чем в хаскеле, но по выразительности - нулевой!
Edited 2012-11-07 16:00 (UTC)

[identity profile] thesz.livejournal.com 2012-11-07 04:09 pm (UTC)(link)
Такое делают все, кто хочет разобраться с ленивыми вычислениями. Конечно же, вы не рассмотрели, допустим, списки. Или деревья. С помощью такого приема вы не сделаете их без переписывания.

Насчет Алгола вы совершенно правы. Я тоже усмехнулся, когда прочитал про call-by-need.

[identity profile] isorecursive.livejournal.com 2012-11-07 06:32 pm (UTC)(link)
@ Такое делают все, кто хочет разобраться с ленивыми вычислениями
Просто в других энергичных языках для этого надо будет всюду ручками писать delay/force.

@ С помощью такого приема вы не сделаете их без переписывания.
Сделаем: http://www.scalakata.com/509aa82be4b093f3524f3a16

[identity profile] thesz.livejournal.com 2012-11-07 07:10 pm (UTC)(link)
>Сделаем

Это и называется "переписать все".

>Просто в других энергичных языках для этого надо будет всюду ручками писать delay/force.

В C# точно нет.

[identity profile] isorecursive.livejournal.com 2012-11-07 07:33 pm (UTC)(link)
@ Это и называется "переписать все".
Не понял. Если речь о "~>", его семантика не изменилась - просто я нашёл более краткий синтаксис, который захватывает отложенную инициализацию, которую я делал вручную.
Updated: а, понятно. Я сначала подумал, что под переписыванием подразумевается мутабельность на завязывании узлов. Но в текущем смысле, это какое-то странное замечание. Вы разве не вкурсе, что в хаскеле любую структуру данных реализуют отдельно в энергичном и ленивом вариантах c полным переписыванием?

@ В C# точно нет.
В C# первый пример кода выглядел бы примерно так:
int lazyInR(int x, Lazy[int] y) = {
  var a = x + 5 * y.Value
  var b = y.Value - 5 * x
  return a * b
}

Lazy[int] x = new Lazy[int](() => {
  Console.WriteLine("I'm printed once!");
  return 2;
});

lazyInR(1, x)

То есть, вручную надо погружать термы типа T в Lazy[T], и вручную вызывать Value.
Edited 2012-11-07 19:42 (UTC)

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-07 19:47 (UTC) - Expand

[identity profile] valentin budaev (from livejournal.com) 2012-11-09 04:20 am (UTC)(link)
> Если вы не знали, то вот вам пример (я уж задолбался его приводить):

Такой if в любом языке пишется, достаточно будет обернуть аргументы в delay. С другой стороны, в хаскеле нельзя реализовать свой begin - он в хаскеле встроен (и называется case). То есть в хаскеле приходится руками оборачивать выражения в force. Это известная эквивалентность ленивого и энергичного порядка. На языке теорката она выражается в том, что в категории соответствующей ленивому порядку нету сумм, а в категории, соответствующей энергичному порядку - произведений, т.о. полученные категории дуальны.

[identity profile] thesz.livejournal.com 2012-11-09 09:25 am (UTC)(link)
В принципе, вы подтвердили мою мысль, но не до конца. Важно количество обёрток.

В строгом языке - все аргументы, в ленивом - в нескольких местах.

[identity profile] valentin budaev (from livejournal.com) 2012-11-10 09:37 am (UTC)(link)
В ленивом языке тоже все аргументы приходится оборачивать. Я же говорю - категории дуальны. То есть соответствие между теми местами, где оборачиваем в лени, взаимооднозначно с теми, где оборачиваем в энергичном языке. Против математики не попрешь :)

[identity profile] thesz.livejournal.com 2012-11-10 10:45 am (UTC)(link)
Вы забываете про видимое программисту. В ленивом языке программисту необходимо изменить меньшее количество мест для создания энергичного кода.

[identity profile] valentin budaev (from livejournal.com) 2012-11-11 12:57 am (UTC)(link)
Я же говорю - мест столько же.
В ленивом языке надо всегда ставить force вокруг первого аргумента кейза, В энергичном - delay вокруг веток if. Но поскольку и там и там это делает невыразимая в рамках языка спецформа (case и if соответственно)., то на практике ни там ни там никто вообще ничего не ставит. Важно, что ситуация совершенно зеркальна.

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

[identity profile] thesz.livejournal.com 2012-11-11 01:23 am (UTC)(link)
Вот числа Фибоначчи:

fibs = 0:1:zipWith (+) fibs (tail fibs)

Я пишу всего две функции, которые будут считать строго голову и строго хвост и сделаю список чисел Фибоначчи полностью строгим.

http://thesz.livejournal.com/906786.html

fibsAsBottom = strictTail $ strictHead $ fibs

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

[identity profile] valentin budaev (from livejournal.com) 2012-11-11 03:10 am (UTC)(link)
> Сколько и чего вам нужно будет написать, чтобы сделать из бесконечного энергичного списка чисел Фибоначчи ленивый?

Я не понял вопроса. Что из чего следует сделать? Известно, что "энергичные бесконечные списки" не существуют. Как из того, что не существует, можно сделать нечто существующее?

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

Напоминаю, о чем мы говорили - на каждый if/case в энергичном/ленивом языке надо ставить delay/force, иначе ничего не будет работать. Т.о. места обязательных расстановок lazy/strict аннотаций строго совпадают. При чем эти аннотации расставляются автоматически, соответствующей спец-формой (if/case). В результате в коде никаких аннотаций нет. Ни в ленивом ни в энергичном случае. Это подтверждается экспериментально - мы спокойно пишем код безо всяких аннотаций. Потому в большинстве языков даже и возможности расставить аннотации руками нет - это просто никому не нужно.

Теперь, еще раз, что вы хотите доказать и как это соотносится с числами Фибоначчи?

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-11 10:04 (UTC) - Expand

[identity profile] valentin budaev (from livejournal.com) 2012-11-11 03:31 am (UTC)(link)
Собственно, прочитав по ссылке:
> По моим соображениям, при энергичном порядке вычислений по умолчанию и ленивости в виде аннотаций, эти самые аннотации надо расставлять везде, где по дизайну программы могут проходить ленивые значения и их надо сохранить ленивыми. Это практически все библиотечные функции, по-моему. А также очень много мест в коде программы - еще раз подчеркну: везде, где по дизайну программы могут проходить ленивые значения и их надо сохранить ленивыми.

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

Ваш аргумент можно было бы принять, но вы подразумеваете тут полную бесконтрольность - то есть у нас-де ВНЕЗАПНО может куда-нибудь прийти санка, а раз может...
Но такой ситуации быть не должно, конечно же - все места, куда должно прийти ленивое вычисление, должны быть фиксированы заданием соответствующего типа. f: Promise Int -> Int - может прийти санка, f : Int -> Int - не может прийти санки. Делаем Promise монадой и обеспечиваем автоматический лифтинг ф-й в нее - в результате весь код будет выглядеть одинаково - и ленивый и энергичный, а нам лишь надо будет в граничных местах заворачивать или вытаскивать значение из lazy-монады где это явно надо.

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

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-11 10:18 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-12 05:47 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-12 06:27 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-12 08:55 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-12 09:00 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-12 11:46 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-12 19:25 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-13 02:23 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-13 04:19 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-14 03:46 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-14 08:21 (UTC) - Expand

(no subject)

(Anonymous) - 2012-11-15 04:46 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-15 10:21 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-15 12:21 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-15 20:44 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-16 02:12 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-16 08:31 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-16 09:54 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-16 11:00 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-16 11:36 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-16 13:08 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-16 13:45 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-16 17:47 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-17 03:41 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-17 04:05 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-17 07:18 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-17 18:01 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-18 04:03 (UTC) - Expand

(no subject)

[identity profile] migmit.livejournal.com - 2012-11-18 11:11 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-25 12:53 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-13 10:20 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-14 03:49 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-14 08:29 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-15 04:54 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-15 08:53 (UTC) - Expand

(no subject)

[identity profile] berezovsky.livejournal.com - 2012-11-15 09:16 (UTC) - Expand

(no subject)

[identity profile] thesz.livejournal.com - 2012-11-15 10:54 (UTC) - Expand

(no subject)

[identity profile] valentin budaev - 2012-11-15 12:01 (UTC) - Expand

[identity profile] valentin budaev (from livejournal.com) 2012-11-11 03:38 am (UTC)(link)
> Модифицировать map мне не надо, а значит, выше модульность.

Фактически, вы сделали следующее:
map = tmp
теперь если я хочу сделать map энергичным, то просто переопределяю tmp, а реализацию map менять не надо!

Ну так это работает и с энергичным и с ленивым порядком :)

[identity profile] thesz.livejournal.com 2012-11-11 01:29 am (UTC)(link)
Вдогонку.

Ленивое ЛИ Тьринг полно. Значит, в нем можно реализовать force.

В приведенных мной примерах это и делается.

(no subject)

[identity profile] valentin budaev - 2012-11-11 03:01 (UTC) - Expand