Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, InCus, Вы писали:
IC>>В эту сказку для детей я не верю. В случае, когда вот так x |> f мы можем вывести тип, а вот так f x внезапно разучились, рассказы об ограничениях .net библиотек выглядят мягко говоря странно.
K>Это не сказка, а суровая правда. Раз система типов расширена, чистый Х-М не работает, и по всей видимости, нужно как-то изворачиваться, используя эвристики. Эвристики всех случаев не покрывают, разумеется, и не во всех случаях ведут к правильному решению.
Сказка даже хорошо пропиареная все равно остается сказкой.
Разницы, для нормальной системы вывода типов, между
typeof<int>.GetMembers() |> Array.map (fun e -> e.Name)
и
Array.map (fun e -> e.Name) <| typeof<int>.GetMembers()
нет. Но в F# это не так.
Но, если проанализировать это http://stackoverflow.com/questions/3162387/why-is-fs-type-inference-so-fickle/3162832#3162832
И отбросить байку Брайена от мифических трудностях ООП в Net (см п.3), то окажется, что
1) Такое убожество родили намеряно, для понижения порога вхождения
2) Ocaml с нормальным выводом типов и объектной моделью в себе не имеет такой болезни
3) Сайм, сам признался, что сделать более внятный вывод типов можно, но его ломает анализировать ошибки
В соотвествии с п.3., на рассказы о том, что не работает из-за того, что "система типов расширена" можно не обращать внимание.
А теперь смертельный номер. Берем
Array.map (fun e -> e.Name) <| typeof<int>.GetMembers() |> printfn "%A"
разумеется не компилится. Выносим (fun e -> e.Name) в виде инлайна
Опа! Чудо! Фактически для системы вывода типов не было сделано ни одной! подсказки,
(и (fun e -> e.Name) и GetName имеют тип ^a -> ^b), но все заработало.
Самое смешное в происходящем, что компилятор мог бы автоматически выносить
(fun e -> e.Name) в GetName и никаких бы проблем с анализом ошибок при этом не произошло,
но это же делать надо, а у нас п.3.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, Klapaucius, Вы писали:
IC>>>вспомнилось еще совершенно отвратильное развертывание хвостовой рекурсии в циклы.
K>>Например?
H>Вероятно речь идет о tail call, который невероятно тормозит на x86.
Может речь об этом?
let rec fac n a x =
match n with
| 1 -> a
| _ -> fac (n-1) (a * n) x
[CompilationArgumentCounts(new int[] { 1, 1, 1 })]
public static int fac<a>(int n, int a, a x)
{
while (true)
{
switch (n)
{
case 1:
return a;
}
x = x; // Надо или не надо, все параметры переприсваиваются на очередной итерации.
a *= n;
n--;
}
}
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, Klapaucius, Вы писали:
IC>>>вспомнилось еще совершенно отвратильное развертывание хвостовой рекурсии в циклы.
K>>Например?
H>Вероятно речь идет о tail call, который невероятно тормозит на x86.
tail call отдельная песня и к F# отношение вряд ли имеет
А вот то, что простейшие вещи
let rec y a b c =
match a with
| 0 -> b
| _ -> y (a - 1) (b + c) c
в лучшем случае проседают на 5-10%, по сравнению с мутабельным циклом, уже не радует.
F# упорно делает c = c; в середине цикла. Вопрос зачем? Уж такую то элементарщину (чай не клиновский Graph Rewriting) можно было сделать?
Здравствуйте, samius, Вы писали:
S>Может речь об этом?
S>
S>let rec fac n a x =
S> match n with
S> | 1 -> a
S> | _ -> fac (n-1) (a * n) x
S>
S>
S>[CompilationArgumentCounts(new int[] { 1, 1, 1 })]
S>public static int fac<a>(int n, int a, a x)
S>{
S> while (true)
S> {
S> switch (n)
S> {
S> case 1:
S> return a;
S> }
S> x = x; // Надо или не надо, все параметры переприсваиваются на очередной итерации.
Здравствуйте, D. Mon, Вы писали:
DM>Здравствуйте, InCus, Вы писали:
IC>>F# упорно делает c = c; в середине цикла.
DM>А разве JIT это не выкинет?
А должен? В лучшем случае на NOPы заменит, а кеши и такты не резиновые.
Ключевой вопрос в том какого F# вообще гадит подобными конструкциями,
и почему за него должен расплачиваться JIT? JIT в отличии от F# не
позиционировался как интелектуальный оптимизарующий компилятор, насколько я помню.
Здравствуйте, InCus, Вы писали:
IC>Ключевой вопрос в том какого F# вообще гадит подобными конструкциями, IC>и почему за него должен расплачиваться JIT? JIT в отличии от F# не IC>позиционировался как интелектуальный оптимизарующий компилятор, насколько я помню.
Здравствуйте, InCus, Вы писали:
IC>Сказка даже хорошо пропиареная все равно остается сказкой. IC>Разницы, для нормальной системы вывода типов, между
typeof<int>.GetMembers() |> Array.map (fun e -> e.Name)
и
IC>Array.map (fun e -> e.Name) <| typeof<int>.GetMembers()
IC>
нет. Но в F# это не так.
Правильно, я и говорю — система ненормальная и нормальной никогда не будет.
IC>2) Ocaml с нормальным выводом типов и объектной моделью в себе не имеет такой болезни
Объектная модель Окамла проектировалась под вывод типов. Объектная система .net — нет.
IC>3) Сайм, сам признался, что сделать более внятный вывод типов можно,
... но он не сделан, а значит утверждение о том, что это будто бы можно — ничем не обоснованное заявление.
IC>но его ломает анализировать ошибки
Не его. Он пишет что не сможет написать вывод типов, обрабатывающий такие случаи так, чтоб сообщения об ошибках вывода типов были понятными.
IC>В соотвествии с п.3., на рассказы о том, что не работает из-за того, что "система типов расширена" можно не обращать внимание.
Если бы система типов не была расширена — ничего особенного Сайму изобретать и не пришлось.
IC>Самое смешное в происходящем, что компилятор мог бы автоматически выносить IC>(fun e -> e.Name) в GetName и никаких бы проблем с анализом ошибок при этом не произошло, IC>но это же делать надо, а у нас п.3.
Единичный пример не доказывает возможности делать это в общем случае.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476>>
'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
Здравствуйте, VladD2, Вы писали:
K>А какие ошибки нужно исправить по вашему мнению? VD>Макросы и C-подобный синтаксис.
К счастью эти ошибки в F# уже исправлены. Осталось убрать ООП, добавить ссылочную прозрачность и нормальное type-level программирование (можно начать с добавления hihg-kinded типов).
Здравствуйте, Klapaucius, Вы писали:
K>Ну, вывод типов в Немерле локальный и не выводит наиболее общий тип. При таких условиях тип можно выводить и при наличии сабтайпинга и перегрузок.
Это тут не причем. Перегрузки реально мешают. А вот вывод обобщенного типа или конкретного на сложность не влияет. В F# банально слабый алгоритм. Там используется не сильно модифицированный Хиндли-Милнер.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Klapaucius, Вы писали:
K>Это не сказка, а суровая правда. Раз система типов расширена, чистый Х-М не работает, и по всей видимости, нужно как-то изворачиваться, используя эвристики. Эвристики всех случаев не покрывают, разумеется, и не во всех случаях ведут к правильному решению.
Нужно просто делать вывод типов в несколько проходов. Москаль описал алгоритм почти 7 лет назад. Могли бы за 7 лет уже и прочесть. Тем более что москаль во всю использует F# и работает в МС.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Klapaucius, Вы писали:
K>>Ну, вывод типов в Немерле локальный и не выводит наиболее общий тип. При таких условиях тип можно выводить и при наличии сабтайпинга и перегрузок.
VD>Это тут не причем. Перегрузки реально мешают. А вот вывод обобщенного типа или конкретного на сложность не влияет. В F# банально слабый алгоритм. Там используется не сильно модифицированный Хиндли-Милнер.
А почему алгоритм Хиндли-Милнера слабый? Я помню что там вариантность generic параметров не поддерживается, что-то еще, но это не такая большая цена за глобальный вывод типов. Мне кажется, что это просто такой tradeoff. В конечном счете, важнее всего читаемость кода, а в этом плане у F# все немного лучше чем у конкурентов, по крайней мене чисто субъективно, с моей колокольни.
Здравствуйте, Lazin, Вы писали:
L>А почему алгоритм Хиндли-Милнера слабый?
Ну, примитивный он. Что тут еще можно сказать то? Много ограничений. Много случаев когда он не работает. Классика F#-а — f g не срабатывает, в то время как g |> f срабатывает. Потом этот алгоритм не дружит с перегрузкой и приведением типов.
L>Я помню что там вариантность generic параметров не поддерживается, что-то еще, но это не такая большая цена за глобальный вывод типов.
Дык глобальный то вывод типов по жизни никому не нужен. А вот качество вывода типов нужно везде и всегда.
L>Мне кажется, что это просто такой tradeoff. В конечном счете, важнее всего читаемость кода, а в этом плане у F# все немного лучше чем у конкурентов, по крайней мене чисто субъективно, с моей колокольни.
У F# с выводом типов все довольно плохо. О чем тут народ и жалуется. Я не понимю как можно влезть в тему где народ жалуется на вывод типов в F# и при этом как не в чем не бывало утверждать обратное.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
L>>А почему алгоритм Хиндли-Милнера слабый?
VD>Ну, примитивный он. Что тут еще можно сказать то? Много ограничений. Много случаев когда он не работает. Классика F#-а — f g не срабатывает, в то время как g |> f срабатывает.
А можешь показать, как конкретно этот баг получается в Хиндли-Милнере? По-моему, никак не должен.
VD>Дык глобальный то вывод типов по жизни никому не нужен.
Зря ты так, очень даже нужен. Т.е. ты, может, и привык без него обходиться, но мой опыт с окамлом говорит, что когда такой вывод типов есть, это очень хорошо и удобно. У меня в компиляторе на две с лишним тыщи строк очень мало где у функций задан явно тип, для подавляющего большинства он просто выводится. А там, где задан, это не из необходимости, а для задания "центра кристаллизации".
Здравствуйте, D. Mon, Вы писали:
DM>Зря ты так, очень даже нужен. Т.е. ты, может, и привык без него обходиться, но мой опыт с окамлом говорит, что когда такой вывод типов есть, это очень хорошо и удобно. У меня в компиляторе на две с лишним тыщи строк очень мало где у функций задан явно тип, для подавляющего большинства он просто выводится. А там, где задан, это не из необходимости, а для задания "центра кристаллизации".
В свое время меня заинтересовали этим языком, сказав что код на нем очень легко читается. Я проверил, взял исходники первого попавшегося проекта, написанного на ocaml который мне удалось найти и начал читать. Так вот, код действительно читается очень хорошо, даже человеком не знакомым с языком. Во многом благодаря тому что очень мало всяких аннотаций и прочего синтаксического мусора. Плюс, паттерн маттчинг и алгебраические типы данных — очень интуитивны и опять же читаются легко. Так что глобальный вывод типов — штука полезная.