Пришло по гугловской рассылке: Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
Здравствуйте, Ka3a4oK, Вы писали:
KK>Пришло по гугловской рассылке: KK>Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
Здесь можно взять презентацию в формате Power Point.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Ka3a4oK, Вы писали:
KK>Пришло по гугловской рассылке: KK>Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
Вот всё-таки скаловская unified type system — необычайно красивая и толковая вещь. Тяжко вздыхал, когда Влад объяснял про when, и ещё более тяжко, когда экспериментировали и ржали над def a = 1 + throw Exception(). На скале ответ простой и однозначный безо всяких экспериментов. Правда, с нею невозможно запретить приведение к Object (в скале — к Any) в профилактических целях, но настолько ли это существенно? Не, я нередко в сложных функциональных выражениях задаю тип результата, чтобы себя проконтролировать — защита от случайных косяков в логике, проявляющихся как Any в типопараметрах. Но гораздо чаще я задаю типы результатов методов просто ради читабельности кода.
Ещё возник вопрос про module. Скаловские object (синглтоны) — это объекты, их можно наследовать из классов и передавать по ссылке. Я так понимаю, с module такого счастья не будет? На возможный вопрос "зачем они нужны, синглтоны — зло", вот такой юз-кейз:
Очень компактный и легкочитаемый код, безо всяких наворотов с SPI. Если потребуется прикрутить к BL автоматическое тестирование, правок минимум:
trait DAL {
def foo() { ... }
...
}
object DAL extends DAL {
}
trait BL(dal: DAL) {
def foo() { dal.foo() }
}
object BL extends BL(DAL)
// Код, использующий синглтон BL, не поменялся.
object Index extends Page {
def service(rq: Request): Response = {
BL.foo()
HTMLResponse(rq, <p>Hello world</p>)
}
}
// Для тестирования BL, создаётся экземпляр с mock-реализацией DAL.
object DALMock extends DAL {
override def foo() { ... }
}
object BLTest {
val bl = new BL(DALMock)
}
Короче, господа, синглтоны рулят! Если это правильные синглтоны. Интересно услышать критику данного подхода, в т.ч. описание ситуаций, когда он не сработает или выйдет себе дороже, etc.
Я так подозреваю, эти скаловские синглтоны наверняка можно без особого геморроя реализовать через макросы, как и case-классы. А что насчёт множественного наследования (rich interfaces)? Тут я не вижу (хотя мои познания весьма поверхностны). Вообще, насколько реально сделать конвертер scala -> nemerle, результат работы которого выглядел бы максимально структурно похожим на scala-код? Вот эти rich interfaces и unified type system выглядят проблемой.
Здравствуйте, dimgel, Вы писали:
D>Вообще, насколько реально сделать конвертер scala -> nemerle, результат работы которого выглядел бы максимально структурно похожим на scala-код? Вот эти rich interfaces и unified type system выглядят проблемой.
И ещё, правильно ли я понимаю, что полиморфизм второго ранга это def f[T[X]]?
Влад, что ты имел в виду, говоря что в скале "типы перадются по ссылке" и про value-типы? Можешь разжевать чуток, как оно устроено?
Здравствуйте, dimgel, Вы писали:
D>Я так подозреваю, эти скаловские синглтоны наверняка можно без особого геморроя реализовать через макросы, как и case-классы.
Паттерн-матчинг в скале можно проводить по любому классу, добавив в его синглтон-компаньон метод unapply(). Похоже, я тут про лёгкость реализации на макросах погорячился, в N — только специальный тип варианта.
Здравствуйте, Ka3a4oK, Вы писали:
KK>Пришло по гугловской рассылке: KK>Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
Хочу сразу напугать. Это только половина (ну, или 2/3). Еще должна быть часть посвященная макросам.
Ну, и приношу прощения за затянутость. Добрые следопыты задали много интересных вопросов, но тем не менее затянули процесс. К тому же я уже сильно отупел к концу и начал повторяться и уходить от темы.
Однако надеюсь, что доклад будет кому-то интересен и подтолкнет к изучению.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, dimgel, Вы писали:
D>Вот всё-таки скаловская unified type system — необычайно красивая и толковая вещь. Тяжко вздыхал, когда Влад объяснял про when, и ещё более тяжко, когда экспериментировали и ржали над def a = 1 + throw Exception(). На скале ответ простой и однозначный безо всяких экспериментов.
Похоже ты просто не понял сказанного. В этой части системы типов немерла и скалы не отличаются. У скалы есть интересные моменты, но это связано с намного более глубокими особенностями (Existential type и т.п.).
D>Правда, с нею невозможно запретить приведение к Object (в скале — к Any) в профилактических целях, но настолько ли это существенно?
Не не важно, если тебя не волнует качество твоих программ и внятность диагностики ошибок. Но очено важно, если волнует.
Что до "невозможно запретить", то это конечно домыслы. Если хочется, то можно все. От системы типов это никак не зависит.
D>Не, я нередко в сложных функциональных выражениях задаю тип результата, чтобы себя проконтролировать — защита от случайных косяков в логике, проявляющихся как Any в типопараметрах. Но гораздо чаще я задаю типы результатов методов просто ради читабельности кода.
Это вообще к делу не относится. Речь шла о выведении общего типа. object в Немерел и Any в Скале выведутся всегда, даже если типы не имеют ничего общего, просто потому, что в языках есть понятие общего корня иерархии типов. Вывод object / Any в 99% случаев (точнее чаще) не имеет ни малейшего смысла для программиста. Так что Немерл просто защищает программиста от глупых и досадных, случайных ошибок.
D>
D>Ещё возник вопрос про module. Скаловские object (синглтоны) — это объекты, их можно наследовать из классов и передавать по ссылке. Я так понимаю, с module такого счастья не будет?
Да. Модуль не синглотн. Данный паттетрн в язык не встроен. Если надо, его можно реализовать вручную или воспользовавшись макросом Singleton
D>На возможный вопрос "зачем они нужны, синглтоны — зло",
Они зло в многопоточном окружении. Это не более чем замаскированное глобальное, разделяемое состояние. Хотя если использовать паттетр с умом, то он может быть полезен.
module же это совсем из другой оперы. Это просто синтаксический сахар для классов которые могут содержать только статические члены.
D>Очень компактный и легкочитаемый код, безо всяких наворотов с SPI.
Ага. Правда, непонятно, что делающий и создающий проблемы в многопоточном окружении, каковым почти наверняка является веб-сервер.
D>Короче, господа, синглтоны рулят!
Но куда-то не туда.
D>Если это правильные синглтоны. Интересно услышать критику данного подхода, в т.ч. описание ситуаций, когда он не сработает или выйдет себе дороже, etc.
Критика очень проста. service может быть вызван параллельно (и не однократно). Без синхронизации код в синглтонах способен натварить кучу бед.
D>
D>Я так подозреваю, эти скаловские синглтоны наверняка можно без особого геморроя реализовать через макросы, как и case-классы.
А зачем реализовывать на макросах case-классы? Чем они от вариантов отличаются?
D>А что насчёт множественного наследования (rich interfaces)?
Множественное наследование интерфейса есть. О миксинах а-ля трэйтсы скалы думали, но как-то не особо они актуальны. А раз нет спроса, то нет и реализации. Спрос на подобные вещи есть у тех кто до этого использовал С++, но после освоения шарпа, похоже люди понимаю, что можно просто использовать другие подходы. И при приходе к немерлу через шарп таких вопросов просто не возникает.
D>Тут я не вижу (хотя мои познания весьма поверхностны). Вообще, насколько реально сделать конвертер scala -> nemerle, результат работы которого выглядел бы максимально структурно похожим на scala-код? Вот эти rich interfaces и unified type system выглядят проблемой.
На много больше проблемой являются разные библиотеки и разные рантаймы.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
D>>Вот всё-таки скаловская unified type system — необычайно красивая и толковая вещь. Тяжко вздыхал, когда Влад объяснял про when, и ещё более тяжко, когда экспериментировали и ржали над def a = 1 + throw Exception(). На скале ответ простой и однозначный безо всяких экспериментов.
VD>Похоже ты просто не понял сказанного. В этой части системы типов немерла и скалы не отличаются. У скалы есть интересные моменты, но это связано с намного более глубокими особенностями (Existential type и т.п.).
Понял, понял. На скале выражение if (false) 10 else throw new Exception() типизируется так: if (Boolean) Int else Nothing, а поскольку Nothing является подтипом всех типов, весь if целиком имеет тип Int. Также, выражение 1 + throw не пройдёт контроль типов, потому что Int.+(x) не имеет overloaded-версии для x типа Nothing. В немерле, ты сам не был уверен, как оно себя поведёт.
D>>Правда, с нею невозможно запретить приведение к Object (в скале — к Any) в профилактических целях, но настолько ли это существенно?
VD>Что до "невозможно запретить", то это конечно домыслы. Если хочется, то можно все. От системы типов это никак не зависит.
Да, тут я попутал — перепутал Nothing, находящийся внизу иерархии, с Any, находящимся вверху. Для унификации вывода типов на throw важен именно Nothing, т.е. от системы типов таки-зависит, но в данном случае запрет на вывод Any действительно никаким боком.
VD>Критика очень проста. service может быть вызван параллельно (и не однократно). Без синхронизации код в синглтонах способен натварить кучу бед.
У меня DAL и BL 100% stateless, так что с этой стороны всё ок.
VD>А зачем реализовывать на макросах case-классы? Чем они от вариантов отличаются?
Ну, например, их принято использовать для entities. Однако у entity могут присутствовать всякие мелкие вспомогательные методы, хотя бы типа def isDeleted = status == 'deleted', а вариант, как я понимаю, — чисто структура.
VD>Множественное наследование интерфейса есть. О миксинах а-ля трэйтсы скалы думали, но как-то не особо они актуальны. А раз нет спроса, то нет и реализации. Спрос на подобные вещи есть у тех кто до этого использовал С++, но после освоения шарпа, похоже люди понимаю, что можно просто использовать другие подходы. И при приходе к немерлу через шарп таких вопросов просто не возникает.
В общем да, на практике не пригождалось.
D>>Вообще, насколько реально сделать конвертер scala -> nemerle, результат работы которого выглядел бы максимально структурно похожим на scala-код? Вот эти rich interfaces и unified type system выглядят проблемой.
VD>На много больше проблемой являются разные библиотеки и разные рантаймы.
Угу, до меня уже дошло, что дурацкая идея. И вообще языки разные.
Здравствуйте, dimgel, Вы писали:
D>Понял, понял. На скале выражение if (false) 10 else throw new Exception() типизируется так: if (Boolean) Int else Nothing, а поскольку Nothing является подтипом всех типов, весь if целиком имеет тип Int. Также, выражение 1 + throw не пройдёт контроль типов, потому что Int.+(x) не имеет overloaded-версии для x типа Nothing. В немерле, ты сам не был уверен, как оно себя поведёт.
Какова связь между моей уверенностью и тем как дела обстоят на самом деле?
Во-первых, я к этому моменту уже был затрахан в доску, а во вторых я не являюсь автором этого кода и не помню/знаю всех его тонкостей.
D>Да, тут я попутал — перепутал Nothing, находящийся внизу иерархии, с Any, находящимся вверху. Для унификации вывода типов на throw важен именно Nothing, т.е. от системы типов таки-зависит, но в данном случае запрет на вывод Any действительно никаким боком.
Если посмотреть внимательно на сообщение об ошибке, то из него четко следует, что компилятор как раз таки вывел object и уже сделав это выдал сообщение об ошибке, так как резонно предположил, что столь общий тип полученный процессе унификации — это наверняка ошибка. Так что это не недоработка системы типов, а огромный плюс компилятора. И если в Скале не так, то тем хуже для ее пользователей.
VD>>Критика очень проста. service может быть вызван параллельно (и не однократно). Без синхронизации код в синглтонах способен натварить кучу бед.
D>У меня DAL и BL 100% stateless, так что с этой стороны всё ок.
Если он 100% stateless, то никакие синглотоны тебе не нужны, так как ту же роль будет выполнять любая функция (статический метод, например). Если же тебе нужен синглтон, значит что-то ты да прикапываешь, а раз так, то грабли ты уже заложил. Осталось дождаться момента когда ты о них забудешь и наступишь на них.
VD>>А зачем реализовывать на макросах case-классы? Чем они от вариантов отличаются?
D>Ну, например, их принято использовать для entities.
Да по барабану для чего их принято использовать кем-то. Вопрос был другим.
D>Однако у entity могут присутствовать всякие мелкие вспомогательные методы, хотя бы типа def isDeleted = status == 'deleted', а вариант, как я понимаю, — чисто структура.
Варианты — это стопроцентная копия case-классов с одним исключением. Все вхождения вариантов всегда делаются sealed. Это аналогично final-case-классам (если я не ошибаюсь в терминологии). Сделано это по понятным причинам. Только в случае запечатанных типов можно проверять паттерн-матчинг по АлгТД на полноту. В Скале же сделан классный косяк (как я понимаю). Кейс-классы по умолчанию не запечатаны и проверки на полноту не производится. Так что надо еще не забыт их запечатать врунчую. Хотя само это дает некоторый простор для маневра.
VD>>На много больше проблемой являются разные библиотеки и разные рантаймы.
D>Угу, до меня уже дошло, что дурацкая идея. И вообще языки разные.
Языки во многом близкие по духу. Многие решения, конечно же, разные. Но это потому что нет одного правильного пути. Выбор дизайнеров языков — это предпочтение одних компромиссов другим. Выбирая одно получаешь одни преимущества и недостатки, вбирая другое — другие. Очень редко можно сделать однозначно правильный выбор.
Скала более академичен. В нем реализованы некоторые крутые (с точки зрения науки) и мало полезные людям, чей уровень находится на уровне уверенного знания шарпа, вещи. Зато плагины к компилятору находятся на уровне плинтуса и в других вопросах есть промахи. Так что на мой взгляд Скала — это очень интересный язык (особенно для исследователей и создателей других языков), но с практической точки зрения немерл лучше. Он ближе к реальным потребностям программистов. Но мое мнение априори предвзятое. Ведь я уже приложил руку к дизайну этого языка.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Если посмотреть внимательно на сообщение об ошибке, то из него четко следует, что компилятор как раз таки вывел object и уже сделав это выдал сообщение об ошибке, так как резонно предположил, что столь общий тип полученный процессе унификации — это наверняка ошибка. Так что это не недоработка системы типов, а огромный плюс компилятора. И если в Скале не так, то тем хуже для ее пользователей.
Это понятно. В скале действительно такой проверки нет, к сожалению. Однако я не об этом писал, а о том, что наличие общего подтипа всех типов позволяет не делать спец. обработки для null и throw.
VD>Если он 100% stateless, то никакие синглотоны тебе не нужны, так как ту же роль будет выполнять любая функция (статический метод, например). Если же тебе нужен синглтон, значит что-то ты да прикапываешь, а раз так, то грабли ты уже заложил. Осталось дождаться момента когда ты о них забудешь и наступишь на них.
Я в своём примере показал, что именно я прикапываю: возможность делать mock-подклассы для юнит-тестов. Статический метод не отнаследуешь.
VD>Варианты — это стопроцентная копия case-классов с одним исключением. Все вхождения вариантов всегда делаются sealed.
Понятно, значит методы в них тоже можно объявлять.
Здравствуйте, Ka3a4oK, Вы писали:
KK>Пришло по гугловской рассылке: KK>Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
Влад, я не был в таком шоке с тех пор, как узнал, что Сова из "Винни-Пуха", на самом деле — Филин. Неужели правда нЕмерле? Я столько лет называл его немЕрле, но только сейчас понял, что нигде и никогда не слышал, как на самом деле произносится это слово
Здравствуйте, kochetkov.vladimir, Вы писали:
KV>Здравствуйте, VladD2, Вы писали:
KV>Влад, я не был в таком шоке с тех пор, как узнал, что Сова из "Винни-Пуха", на самом деле — Филин. Неужели правда нЕмерле? Я столько лет называл его немЕрле, но только сейчас понял, что нигде и никогда не слышал, как на самом деле произносится это слово
Здравствуйте, Ka3a4oK, Вы писали:
KK>Пришло по гугловской рассылке: KK>Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
В скале есть еще traits — суть интерфейсы у которых некоторые методы реализованы.
В немерле сейчас такие интервейсы задавать нельзя, нужно править компилятор (Ошибка iterface cannot have method body definition).
Зато если сгенерить сборку с таким интервейсом через emit то немерле её обрабатывает корректно и
даёт вызвать метод интерфейса.
Здравствуйте, dimgel, Вы писали:
D>Я в своём примере показал, что именно я прикапываю: возможность делать mock-подклассы для юнит-тестов. Статический метод не отнаследуешь.
Для для этих целей в .NET есть инструменты: moles, typemock isolator,
в немерле несложно для интерфейса генерировать mock-объект макросом...
Здравствуйте, rameel, Вы писали:
R>Здравствуйте, Ka3a4oK, Вы писали:
KK>>Пришло по гугловской рассылке: KK>>Часть 1, Часть 2. Посмотрел — интересно, очень живое общение получилось. Хотя из-за этого живого общения не удалось о многом рассказать. (Может, есть третья часть?)
R>А вот собственно и 3 часть
Надо бы все три ссылки на nemerle.org разместить на очень видном месте.