Статья в целом хвалебная, они очень довольны своим выбором языка.
We were lucky with our choice of Haskell and GHC and, in light of our experience, we would make the same choice again.
Но они при этом честно описывают проблемы, с которыми столкнулись (раздел 4, цитирую кусочки, которые мне показались весомыми, но лучше прочитайте раздел целиком, это полторы странички):
1. Language Issues
We found that to use many common Haskell structures, such as functors, monoids and monads, we needed to think in significantly different ways than we did in other languages, even other functional languages such as Scheme.
2. Refactoring
The main issue was that where, with a language such as Ruby, one can change a small handful of functions and leave the rest “broken” while testing the change, in Haskell one must fix every function in the module before even being able to compile.
3 Profiling Tools, and Deficiencies Thereof
One of the major problems, directly connected to lazy evaluation, is that where the work happens is quite a slippery concept.
One module may define and apply all of the functions for the work to be done, but in actuality leave only a few thunks in the heap: the real work of the computation occurs in some other place where the data are used.
It’s quite possible to have a final consumer of “work” actually doing all of the work that one wanted to have done in several other threads running on different cores.
A last note about a particular problem for us with GHC: profiling builds must run using the non-parallel runtime.
4 Lazy Evaluation and Space Leaks
там весь раздел придется цитировать в Standard Chartered они фактически убили ленивость, причем прямо в компиляторе.
Статья 2009 года, так что интересно, изменилось ли что-нибудь за последние 5 лет, как в инструментарии, так и в подходах. Особенно интересуют пункты 2-4. С проблемами рефакторинга в строгой системе типов я сталкивался и в С++: начинаешь менять тип — и нужно испрвить все и везде, пока оно просто скомпилируется. В этом смысле прототипировать на строгих языках очень неудобно — слишком много изменений нужно делать везде на каждый чих в системе типов, чтобы только удовлетворить компилятор — задолго до того, как перейдешь к собственно юнит-тестам, чтобы проверить свои изменения.
Здравствуйте, jazzer, Вы писали:
J>1. Language Issues J>
J>We found that to use many common Haskell structures, such as functors, monoids and monads, we needed to think in significantly different ways than we did in other languages, even other functional languages such as Scheme.
Ну, это не так уж страшно. Пишешь на сиквеле — начинаешь думать по-сиквельному. Пишешь на каком-нибудь орм-фреймворке — отучаешься думать по-сиквельному.
J>2. Refactoring J>
J>The main issue was that where, with a language such as Ruby, one can change a small handful of functions and leave the rest “broken” while testing the change, in Haskell one must fix every function in the module before even being able to compile.
А вот это очень хорошо объяснимо.
Рефакторинг эффективен тогда, когда пишешь код максимально буквально, в соответствии с замыслом "что надо получить", а не "как красиво записать".
А если начинаешь помогать компилятору и сокращ. — вот тут и начинается секс.
Первая причина — обольщение тацитными записями. (И тут, наверно, надо обратиться к опыту программистов на джей-кей-ку — как они обеспечивают рефакторабельность своего кода?)
Вторая причина — связана с борьбой против компилятора (против ленивости). Как я понимаю, она выливается в кучу дополнительных аннотаций, и не дай бог где-то что-то забудешь.
J>4 Lazy Evaluation and Space Leaks J>там весь раздел придется цитировать в Standard Chartered они фактически убили ленивость, причем прямо в компиляторе.
Здравствуйте, jazzer, Вы писали:
J>2. Refactoring J>
J>The main issue was that where, with a language such as Ruby, one can change a small handful of functions and leave the rest “broken” while testing the change, in Haskell one must fix every function in the module before even being able to compile.
J>>The main issue was that where, with a language such as Ruby, one can change a small handful of functions and leave the rest “broken” while testing the change, in Haskell one must fix every function in the module before even being able to compile.
Эээээ. То есть то, о чем кричали большевики, то есть «динамика не нужна, только статическая проверка типов» неверно? В реальной жизни динамика таки нужна?
Здравствуйте, Mamut, Вы писали:
M>Эээээ. То есть то, о чем кричали большевики, то есть «динамика не нужна, только статическая проверка типов» неверно? В реальной жизни динамика таки нужна?
Во процессе рефакторинга — да, нужна. И это не динамика, строго говоря.
M>>Эээээ. То есть то, о чем кричали большевики, то есть «динамика не нужна, только статическая проверка типов» неверно? В реальной жизни динамика таки нужна?
J>Во процессе рефакторинга — да, нужна.
Ну, там две причины указаны (пункт 8.2):
• During prototyping, programmers often comment out partly
written or temporarily out-of-date code, while prototyping
some new part. Commenting out is tiresome because one must
do it consistently: if you comment out f you must comment out
everything that calls f , and so on. Deferring type errors is a kind
of lazy commenting-out process.
• During software evolution of a working system it can be burdensome
to maintain global static type correctness. It may be more
productive to explore a refactoring, or change of data representation,
in part of a software system, and test that part, without
committing to propagating the change globally.
Здравствуйте, Mamut, Вы писали:
J>>Во процессе рефакторинга — да, нужна.
M>Ну, там две причины указаны (пункт 8.2): M>
M>• During prototyping, programmers often comment out partly
M>written or temporarily out-of-date code, while prototyping
M>some new part. Commenting out is tiresome because one must
M>do it consistently: if you comment out f you must comment out
M>everything that calls f , and so on. Deferring type errors is a kind
M>of lazy commenting-out process.
M>• During software evolution of a working system it can be burdensome
M>to maintain global static type correctness. It may be more
M>productive to explore a refactoring, or change of data representation,
M>in part of a software system, and test that part, without
M>committing to propagating the change globally.
Имхо, второй пункт о том же самом — о рефакторинге.
В конечном коде все равно будет полная статическая типовая корректность, в отличие от динамических языков, когда реальная система в продакшене работает в динамике.
Здравствуйте, Mamut, Вы писали:
M>Эээээ. То есть то, о чем кричали большевики, то есть «динамика не нужна, только статическая проверка типов» неверно? В реальной жизни динамика таки нужна?
А там проверка только статическая. Компайл-тайм ошибки типов становятся ворнингами, а неправильные, нетипизируемые статически термы заменяются на кидатели исключений. Вот если бы вставлялись динамические касты, которые проверяют рантайм-тип по RTTI (Typeable, например), которые могут быть и успешными — вот это была бы динамика, т.е. проверка типов в рантайме. А тут тип проверяется только в компайл-тайме, а в рантайме проверяется выполнение нетипизируемого в компайл-тайм кода, которое всегда безуспешно, никакой попытки проверить в рантайме то, что в компайл-тйме непроверяемо не делается.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, jazzer, Вы писали:
J>Статья 2009 года, так что интересно, изменилось ли что-нибудь за последние 5 лет, как в инструментарии, так и в подходах. Особенно интересуют пункты 2-4. С проблемами рефакторинга в строгой системе типов я сталкивался и в С++: начинаешь менять тип — и нужно испрвить все и везде, пока оно просто скомпилируется. В этом смысле прототипировать на строгих языках очень неудобно — слишком много изменений нужно делать везде на каждый чих в системе типов, чтобы только удовлетворить компилятор — задолго до того, как перейдешь к собственно юнит-тестам, чтобы проверить свои изменения.
По 2 уже написали про deferring type errors
По 3 Там проблема не столько из за ленивости, сколько из-за "тяжеловесности" профилирования, искажающей результаты (работа над этим ведется, из самого нового — в 7.10 будет экспериментальная поддержка DWARF) и преобразований кода, из-за которых сложно определить какому работающему коду соответствует какой написанный программистом (работы на этом направлении также ведутся).
По 4: Mu (Standard Chartered-овский хаскелеподобный язык) строгий (энергичный) не от хорошей жизни (как и многие хаскелеподобные языки типа омеги или идриса), ленивость там не "убивают в компиляторе", а просто и не добавляют, обычно из-за существенных сложностей с имплементацией. Ну и его автор, Леннарт Августсон теперь один из самых активных пропонентов ленивости, да и его коллеги высказываются в том смысле, что попробовавшие энергичный хаскель совсем не рады отсутствию ленивости по умолчанию.
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll