Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Дело в том, что boost::variant обеспечивают такую статическую проверку (выше я уже это упоминал). EP>Например, если добавить новый продукт, но не изменив visitor, то будет ошибка компиляции:
А кроме визитора распаковать вариант никак нельзя? Что-нибудь вроде "if (x is Cellphone)"...
Re[13]: Неправильное введение в функциональное программирование
Здравствуйте, AlexRK, Вы писали:
ARK>А кроме визитора распаковать вариант никак нельзя? Что-нибудь вроде "if (x is Cellphone)"...
Справедливости ради, в нетотальных языках вроде хаскеля есть и функции вроде fromJust и tail, которые не заставляют обрабатывать все варианты, а предполагают один, а если там был другой, то происходит рантайм ошибка. Так что тут в некотором смысле паритет. Вот в языках с проверкой тотальности, типа того же идриса, уже построже проверки.
Re[13]: Неправильное введение в функциональное программирование
Здравствуйте, AlexRK, Вы писали:
ARK>А кроме визитора распаковать вариант никак нельзя? Что-нибудь вроде "if (x is Cellphone)"...
Там есть x.which() который возвращает номер текущего типа, и есть x.type(), который возвращает std::type_info.
Плюс есть get<T>(x) — если в X находится объект типа T, то он возвращается, иначе кидается исключение, то есть сделать "внешний" switch/if можно.
Но тут два момента:
1. При необходимости, get можно запретить, сделав wrapper на boost::variant, или полностью свой.
2. Ручной if/switch по части возможных типов, означает что мы сознательно делаем для остальных "match all default", а-ля "_".
Re[14]: Неправильное введение в функциональное программирование
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Там есть x.which() который возвращает номер текущего типа, и есть x.type(), который возвращает std::type_info. EP>Плюс есть get<T>(x) — если в X находится объект типа T, то он возвращается, иначе кидается исключение, то есть сделать "внешний" switch/if можно. EP>Но тут два момента: EP>1. При необходимости, get можно запретить, сделав wrapper на boost::variant, или полностью свой. EP>2. Ручной if/switch по части возможных типов, означает что мы сознательно делаем для остальных "match all default", а-ля "_".
Понятно. Но как-то вот не покидает ощущение, что это все кривоватая эмуляция. Необходимость создания кучи визиторов (и прокидывать в каждый из них необходимый контекст!), и враппер для защиты. Можно ведь, аналогичным образом рассуждая, эмулировать константы в языке, их не имеющем, путем "соглашения не менять определенные переменные". Однако константы от этого в языке не появляются. В общем, на мой взгляд, не тянет это на "прекрасно реализуется".
Re[14]: Неправильное введение в функциональное программирование
Здравствуйте, D. Mon, Вы писали:
DM>Справедливости ради, в нетотальных языках вроде хаскеля есть и функции вроде fromJust и tail, которые не заставляют обрабатывать все варианты, а предполагают один, а если там был другой, то происходит рантайм ошибка. Так что тут в некотором смысле паритет. Вот в языках с проверкой тотальности, типа того же идриса, уже построже проверки.
В таком случае наверное да, все аналогично. У С++ все хуже по части бойлерплейта.
Re[15]: Неправильное введение в функциональное программирование
Здравствуйте, Evgeny.Panasyuk, Вы писали:
DM>>Вот в языках с проверкой тотальности, типа того же идриса, уже построже проверки. EP>А есть ли там возможность сматчить только один тип, а для остальных выполнить default?
Для конечных сумм (вроде Maybe, Either и т.п., где число вариантов фиксировано) можно: _. Даже если потом тип изменится, и в сумме появится новый вариант, функция с _ отработает без рантайм ошибок, что и требовалось.
С более хитрыми типами уже не уверен.
Re[15]: Неправильное введение в функциональное программирование
Здравствуйте, AlexRK, Вы писали:
ARK>Понятно. Но как-то вот не покидает ощущение, что это все кривоватая эмуляция. Необходимость создания кучи визиторов (и прокидывать в каждый из них необходимый контекст!)
Я показал C++1998 код.
В C++11/14 boilerplate при желании убирается:
Wrapper для защиты и не нужен (а если нужен — пишется один раз) — даже в тех языках где нет match-all-default, всегда можно его сделать вручную:
match_only_laptop(Variant x, Action f, Action default)
{
match(Laptop) as laptop: f(laptop)
match(Cellphone) as d : default(d)
match(Desktop) as d : default(d)
}
ARK>Можно ведь, аналогичным образом рассуждая, эмулировать константы в языке, их не имеющем, путем "соглашения не менять определенные переменные". Однако константы от этого в языке не появляются. В общем, на мой взгляд, не тянет это на "прекрасно реализуется".
Константы дают возможность проверить компилятору некоторые инварианты статически.
boost::variant-like также позволяет проверить делать статические проверки — это не какое-то внешнее соглашение, а разговор на вполне понятном компилятору языке.
Re[16]: Неправильное введение в функциональное программирование
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Я показал C++1998 код. EP>В C++11/14 boilerplate при желании убирается:
Да, так лучше. Но визиторов все равно придется писать много, в общем случае по одному на каждый вызов — контекст-то везде разный.
К примеру вот на псевдокоде:
var foo: Integer := ...
var bar: String := ...
case variant
when CellPhone then
print("Phone count: ", foo + 3);
when Laptop then
print(bar, variant.x);
end;
Мы же можем использовать при паттерн-матчинге и локальные переменные, и параметры метода, и поля класса. Все это добро придется передавать в визиторы.
Re[17]: Неправильное введение в функциональное программирование
Здравствуйте, AlexRK, Вы писали:
EP>>Я показал C++1998 код. EP>>В C++11/14 boilerplate при желании убирается:
ARK>Да, так лучше. Но визиторов все равно придется писать много, в общем случае по одному на каждый вызов — контекст-то везде разный.
Так и matcher'ы будут на каждый вызов
ARK>К примеру вот на псевдокоде: ARK>
ARK> var foo: Integer := ...
ARK> var bar: String := ...
ARK> case variant
ARK> when CellPhone then
ARK> print("Phone count: ", foo + 3);
ARK> when Laptop then
ARK> print(bar, variant.x);
ARK> end;
ARK>
ARK>Мы же можем использовать при паттерн-матчинге и локальные переменные, и параметры метода, и поля класса. Все это добро придется передавать в визиторы.
Тут нет проблемы, лямбды умеют захватывать контекст:
Здравствуйте, AlexRK, Вы писали:
EP>>Тут нет проблемы, лямбды умеют захватывать контекст: ARK>А! Понял. Не увидел сразу, что там вызов, а не объявление класса. Да, последний вариант, похоже, является полной аналогией с АТД.
Да. (не считая, как заметил D. Mon, pattern-matching'а — но он и не обсуждался в статье)
Re[20]: Неправильное введение в функциональное программирование
Здравствуйте, D. Mon, Вы писали:
DM>Просто у тебя один вид значений отличался от другого тэгом Some, а здесь для них заведены тэги Nbr и Fn, смысл тот же абсолютно.
Ну, правильно, ты поменял код и устранил зависимость от рантайм значений. И что ты хотел этим доказать? Понятно, что аналогичную программу можно написать на любом языке. Она приводилась в качестве примера реализации.
Re[20]: Неправильное введение в функциональное программирование
Здравствуйте, D. Mon, Вы писали:
DM>Звучит как немерлизм, ни в одном нормальном языке такого нет (чтобы именно первого). Формально это ересь, тип функции тут указан уже при определении, а потом лишь используется. Но если речь просто про полиморфизм, то да, тут одна из его форм, параметризованный тип.
Никакой это не немерлизм. Это, может, в OCaml не так, где не потрудились ХМ допилить. А в языках с полиморфными операторами ты даже функцию "sum x y =x+y" иначе не типизируешь. Ибо какой у нее тип?
ВВ>>Здесь нет зависимости от рантайм значений, ведь эта программа доказывается статически. "Доказывается статически" означает, что возможный диапазон значений нам известен в компайл тайм, и мы можем доказать, что ни при каких случаях не выйдем из этого диапазона. Откуда здесь взяться зависимости от рантайма? Тут ее нет
DM>Ну что значит нет? Значение v тут один раз создается и передается. Какого оно типа? Зависит от того, как мы программу запустили, что ей передали. Это и есть зависимость от рантайма в чистом виде, а не твои странные определения.
Ну да.
x = 2
Это тоже рантайм значение. Вот только известно оно в компайл-тайме.
Повторяю — зависимость от рантайм-значений означает зависимость от значений, которые *известны* только в рантайме.
ВВ>>"Зависимость от рантайм-значений" означает "Зависимость от значений, который становятся *известны* только в рантайме". DM>Ровно так и есть же в этом примере. Есть зависимость типа от булевого значения, истина там или ложь — будет известно только в рантайме.
Диапазон значений известен в компайл, соответственно, всё просчитывается статически.
BB>>А для того, чтобы программа была доказуема статически, ее нужно свести к набору значений, который известен уже в период компиляции. Никаких "нежданчиков" там быть не может. Так что это, мягко говоря, не одно и то же
DM>Да, "набор" значений должен быть известен статически, но он может быть бесконечным. В примере выше, где список читался извне, тип функции зависел от длины списка, который мог быть сколь угодно длинным (если закрыть глаза на технические ограничения ОС). Т.е. в зависимости от переданных данных могло получиться значение типа
Там была зависимость от длины списка. Для доказательства корректности достаточно было того, чтобы длина была длиной именно списка, что компилятор прекрасно видел в коде "length xs" и "fun xs".
DM>Но ты прав, что обо всех этих множествах мы должны быть способны рассуждать статически, именно это зависимые типы и позволяют, описать рантайм зависимость статически. "Нежданчиков" там быть не может быть, потому что любой настоящий "нежданчик" это баг, это то, что твоя программа не способна обработать и упадет или выдаст мусор. Такие баги-нежданчики статическая типизация уничтожает. Незачем позволять компилировать программы с такими явными багами.
Статическая типизация устраняет не баги, а все, что не может просчитать статически. Любую ситуацию с "нежданчиком" можно обработать — на основе данных, которые становятся доступны только в рантайме. Не говоря уж о том, что на практике любая система типов имеет свои ограничения и может не пропускать корректный код просто потому что не способна его доказать.
Re[21]: Неправильное введение в функциональное программирование
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Никакой это не немерлизм. Это, может, в OCaml не так, где не потрудились ХМ допилить. А в языках с полиморфными операторами ты даже функцию "sum x y =x+y" иначе не типизируешь. Ибо какой у нее тип?
над a определен (+) => a -> a -> a
ВВ>Любую ситуацию с "нежданчиком" можно обработать — на основе данных, которые становятся доступны только в рантайме.
Ну так, упрощенно говоря, код, обрабатывающий "нежданчик" — и есть доказательство для теоремы, заданной в сигнатуре типа. Вот компилятор и проверяет обрабатываются ли все "нежданчики". А если такого кода нет — это баг.
'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
Re[22]: Неправильное введение в функциональное программирование
Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, Воронков Василий, Вы писали: ВВ>>Никакой это не немерлизм. Это, может, в OCaml не так, где не потрудились ХМ допилить. А в языках с полиморфными операторами ты даже функцию "sum x y =x+y" иначе не типизируешь. Ибо какой у нее тип?
K>
K>над a определен (+) => a -> a -> a
K>
А что такое "а"? С чего ты решил, что функция полиморфная?
> let sum x y = x+y
sum "Hello," "there!";;
val sum : x:string -> y:string -> string
val it : string = "Hello,there!"
> let sum x y = x+y
sum 1 2;;
val sum : x:int -> y:int -> int
val it : int = 3
Да и, собственно, если даже она полиморфная, ее все равно компилировать как-то надо — хотя это уже другой вопрос.
Но на всякий случай повторюсь, чтобы не раздувать лишний флейм — речь шла о том, что "типизация по месту" это прямь какая-то кривь и немерлизм. Однако нет. Нормальная практика.
ВВ>>Любую ситуацию с "нежданчиком" можно обработать — на основе данных, которые становятся доступны только в рантайме. K>Ну так, упрощенно говоря, код, обрабатывающий "нежданчик" — и есть доказательство для теоремы, заданной в сигнатуре типа. Вот компилятор и проверяет обрабатываются ли все "нежданчики". А если такого кода нет — это баг.
Как бы. Но как обычно не всякий код, который справится со своей задачей в рантайме, система типов примет для доказательства в компайл-тайме. Ну и опять же — для успешного доказательства в компайл-тайме может просто быть недостаточно данных.
Или я не прав и круче зависимых типов только яйца?
Re[21]: Неправильное введение в функциональное программирование
ВВ>Это тоже рантайм значение. Вот только известно оно в компайл-тайме. ВВ>Повторяю — зависимость от рантайм-значений означает зависимость от значений, которые *известны* только в рантайме.
args <- getArgs -- читаем аргументы командной строки, рантайм значение
let p = length args > 1
Вы не видите разницы? p — рантайм значение.
Ну и, просто повторите этот код на любом другом языке программирования.
Re[22]: Неправильное введение в функциональное программирование
Здравствуйте, VoidEx, Вы писали:
VE>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>
ВВ>>x = 2
ВВ>>
ВВ>>Это тоже рантайм значение. Вот только известно оно в компайл-тайме. ВВ>>Повторяю — зависимость от рантайм-значений означает зависимость от значений, которые *известны* только в рантайме.
VE>
VE> args <- getArgs -- читаем аргументы командной строки, рантайм значение
VE> let p = length args > 1
VE>
VE>Вы не видите разницы? p — рантайм значение. VE>Ну и, просто повторите этот код на любом другом языке программирования.
У p всего два возможных значения, оба известны в компайл и зависимости от рантайма тут нет.
Re: Неправильное введение в функциональное программирование
Здравствуйте, Воронков Василий Владимирович, Вы писали:
ВВВ>Аннотация: ВВВ>В данном введении я не буду рассказывать об истории функциональных языков программирования. Я не буду писать о лямбда исчислении и комбинаторике. Я даже не буду убеждать читателя в том, что функциональное программирование – это полезно и важно. Наверняка вы уже неоднократно обо всем этом читали. У меня в данном случае несколько иная задача. Я постараюсь действительно ответить на некоторые вопросы, которые могли остаться у вас после прочтения других «введений». Это, конечно, не слишком соответствует традициям – отсюда и подобное название у этой статьи..
Func<int,int> sum(int x)
{
return z => y => x + y + z;
}
Вот это не палит. Сигнатура должна быть Func<int, Func<int, int>>
Re: Неправильное введение в функциональное программирование
Здравствуйте, Воронков Василий Владимирович, Вы писали:
ВВВ>Аннотация: ВВВ>В данном введении я не буду рассказывать об истории функциональных языков программирования. Я не буду писать о лямбда исчислении и комбинаторике. Я даже не буду убеждать читателя в том, что функциональное программирование – это полезно и важно. Наверняка вы уже неоднократно обо всем этом читали. У меня в данном случае несколько иная задача. Я постараюсь действительно ответить на некоторые вопросы, которые могли остаться у вас после прочтения других «введений». Это, конечно, не слишком соответствует традициям – отсюда и подобное название у этой статьи..
Не совсем ясно, почему статья называется "неправильное введение", если у ней отличие от других материалов около нуля. Вопросы которые остаются после книг по ФП обычно навроде "для чего", "где подвох" и "как контролировать память и перформанс"
А у тебя как то скучно — "что это такое" только галопом по европам.