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] golikov konstantine (from livejournal.com) 2012-11-06 06:10 pm (UTC)(link)
А что в эрланговском паттерн матчинге есть, чего нет в скаловском?

мне в голову приходит только паттерн матчинг в аргументах функции, но а) это в некотором виде существует в виде structural typing б) синтаксический сахар и аналог без сахара в скале есть

[identity profile] divine-assass1n.livejournal.com 2012-11-06 06:21 pm (UTC)(link)
Да, в аргументах функции, плюс, как тут рядом в треде уже развернули дискуссию на эту же тему "многабукаф".

Guards опять же.

Вот например решение простого задания из курса Мартина на coursera, первая неделя, на сбалансированные круглые скобки, только на эрланге:

-module (balance).

-export([balance/1]).

balance(CharList) -> balance(0, CharList).

balance(-1, _) -> false;
balance(0, []) -> true;
balance(_, []) -> false;
balance(Counter, [$(|T]) -> balance(Counter + 1, T);
balance(Counter, [$)|T]) -> balance(Counter - 1, T);
balance(Counter, [_|T]) -> balance(Counter, T).

imho, элегантно и очень читабельно.

Вообще, я не претендую ни на что больше чем субъективное мнение, мой скромный опыт в скале - это почти завершенный курс Мартина на coursera c его assignments и 150 страниц книги. И мнение меняется по мере изучения предмета.

[identity profile] golikov konstantine (from livejournal.com) 2012-11-06 06:24 pm (UTC)(link)
"а необходимость указывать типы во вложенных функциях"
можно вот тут поподробней?

[identity profile] golikov konstantine (from livejournal.com) 2012-11-06 06:30 pm (UTC)(link)
Guards есть http://metaclass.livejournal.com/741714.html?thread=14887506#t14887506

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

Вот например сбалансированные строки на скале

def isBalanced(list: List[Char]) = {
@annotation.tailrec
def countBracets(string: List[Char], count: Int = 0): Int = {
string match {
case '('::tail => countBracets(tail, count+1)
case ')'::tail => countBracets(tail, count-1)
case x::tail => countBracets(tail, count) // just skip if not bracet
case Nil => count
}
}

if (countBracets(list) == 0) true else false
}

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

[identity profile] divine-assass1n.livejournal.com 2012-11-06 06:38 pm (UTC)(link)
Ну что я могу сказать? Мне скала нравится. Когда начинал, не ожидал, что понравится.

[identity profile] sum-erman.livejournal.com 2012-11-06 07:21 pm (UTC)(link)
def foo(l: List[A]): Boolean = {
def bar(x: A): A = {
...
bar(..)
}
....
}

Для bar надо всегда указывать типы формальных параметров, а если она рекурсивная, то и тип возвращаемого значения. А в цацкеле в let и where — не надо.

[identity profile] golikov konstantine (from livejournal.com) 2012-11-06 07:24 pm (UTC)(link)
ясно понятно, спасибо

выше про это вроде бы было (но если что -- основная причина -- в хаскелле хиндли милнер)

это и вправду не обойти, увы

[identity profile] stdray.livejournal.com 2012-11-06 08:04 pm (UTC)(link)
Лямбды тоже без замыканый вроде обещались быть.

[identity profile] sum-erman.livejournal.com 2012-11-06 08:23 pm (UTC)(link)
Я понимаю (примерно) почему оно так как оно есть, но писать код от этого увы не проще и не удобнее :/

[identity profile] sum-erman.livejournal.com 2012-11-06 08:46 pm (UTC)(link)
Я честно хотел написать нормальный пример, но мне лень. Попытаюсь кратко обрисовать на словах.

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

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

P.S.: окончательного мнения "нужно ли вообще тащить объекты?" я ещё не выработал.

[identity profile] sum-erman.livejournal.com 2012-11-06 09:09 pm (UTC)(link)
Это если с ОО стороны смотреть. С функциональной чего я только не видел — коллекции (например) делают и из class'ов и из trait'ов. "Единичные" значения делают и как val и как object. Голова кругом %)

[identity profile] thesz.livejournal.com 2012-11-06 09:13 pm (UTC)(link)
weight '(' = 1
weight ')' = -1
weight x = 0
balanced = check . scanl (+) 0 . map weight
    where
        check balances = all (>=0) balances && last balances == 0
Не мог удержаться. ;)

[identity profile] metaclass.livejournal.com 2012-11-06 09:22 pm (UTC)(link)
Ну вот зачем вы издеваетесь над людьми своим лаконичным хаскелем? :)
После скалы это выглядит как бальзам на душу.

[identity profile] thesz.livejournal.com 2012-11-06 09:28 pm (UTC)(link)
Мне можно. Я уже давно потерян для программистского общества. ;)

Это ещё и демонстрация, чем выгодна ленивость. В качестве бонуса. ;)

[identity profile] thesz.livejournal.com 2012-11-06 09:30 pm (UTC)(link)
Кстати, у вас код пропустит строку ")(". Код на Эрланге и мой не пропустят.

Мелочь, а приятно. ;)

[identity profile] golikov konstantine (from livejournal.com) 2012-11-06 10:45 pm (UTC)(link)
плюсую
и вправду красиво и компактно

[identity profile] andy legkiy (from livejournal.com) 2012-11-06 11:19 pm (UTC)(link)
Сравнивают конкретный пример с рекурсией, так надо обязательно выставить HOF. Будто их больше нигде нет.
def balanced(seq: Seq[Char]) = {
  val balances = seq.view.map {
    case '(' => 1
    case ')' => -1
    case _ => 0}.scanLeft(0)(_+_)
  balances.forall(_ >= 0) && (balances.last == 0)
}

[identity profile] isorecursive.livejournal.com 2012-11-07 03:12 am (UTC)(link)
Ну вот объекты, классы, трейты, и прочие модули, начинаются там, где приходит понимание, почему так делать не удобно. А вообще да, можно и числа лямбдами закодировать.

[identity profile] isorecursive.livejournal.com 2012-11-07 05:38 am (UTC)(link)
В хаскеле из всего этого есть только экзистенциальные типы, а в статье описывается методика проектирования постепенно уточняемых при сабтайпинге первоклассных параметризуемых экзистенциальных модулей, и их композиции. В хаскеле, к сожалению, практически отсутствует поддержка каких-либо концепций для programming-in-large.

[identity profile] thesz.livejournal.com 2012-11-07 08:03 am (UTC)(link)
Что такое programming in large?

Могут ли модули быть заменены записями? (подсказка: Cayenne)

[identity profile] thesz.livejournal.com 2012-11-07 08:23 am (UTC)(link)
Есть. Но не столь краткие и не так хорошо разделяют обязанности.

[identity profile] thesz.livejournal.com 2012-11-07 08:26 am (UTC)(link)
Да, ещё вот, что надо заметить. Вот в этой статье https://www.precog.com/blog-precog-2/entry/existential-types-ftw есть список успехов: "Limited-to-no "classical" OO", "Most-Specific type at the "end of the world"" и т.д.

Я про эти успехи говорил.

[identity profile] xeno-by.livejournal.com 2012-11-07 08:37 am (UTC)(link)
Разделяют обязанности?

[identity profile] divine-assass1n.livejournal.com 2012-11-07 08:38 am (UTC)(link)
http://ro-che.info/ccc/17.html :)

Page 3 of 7