Здравствуйте, dotneter, Вы писали:
D>Здравствуйте, artelk, Вы писали:
D>Вроде все тоже самое, можно было остановится на
A>>let bar i = i + 1 A>>let bar s = s + "\n"
D>bar :: int | string -> int | string
D>Тут же как раз работает матчинг по типу.
Ага. Но разные bar могут лежать в разных "сборках".
Именно такой тип конструируется для bar, хотя, например, "bar 1" все еще должен вернуть int, а не анонимный int|string.
Если оба типа принадлежат одному классу типов, то тоже можно было бы создавать такой bar по месту:
class Plusable a where
(+) :: a -> a -> b
instance Plusable Int where
...
instance Plusable String where
...
bar :: (Plusable a) => a -> a -> a
let bar (Plusable x) = x + x + x
let z1 = bar x1
z1 :: int | string
ЗЫ (С) Слабо-сильно типизированный язык программирования
Re: Анонимные алгебраические типы. Более сложный случай.
Здравствуйте, dotneter, Вы писали:
D>Давече господину Воронкову Василию был задан вопрос, зачем нужен динамик. D>Что бы возвращать разные типы из функции в записимости от входящих параметров, отвечает он.
Исходный пример я пропустил. Он мне не нравится. Предлагаю более интересный вариант (из практики):
public static function exceptionProtect(f : Function, exnHandler : Function) : Function {
return function(...args) : * {
try {
return f.applly(null, args);
} catch (e : *) {
exnHandler(e);
throw e;
}
}
}
Эта функция принимает функцию (с произвольным числом и типом аргументов, более точной типизации функций в языке нет) и возвращает функцию, которая добавляет дополнительный уровень обработки исключений. Для всех возникших в f исключений вызывается exnHandler и исключение пробрасывается дальше. Работает для всех функций — с любым числом и типами аргументов, корректно работает с опциональными параметрами. Вопрос: а как оно будет выглядеть в вашем статически типизированном решении?
D>Я предлагаю статически типизированое решение, что то похожее на D>
D>let foo x = if x == 0 then 0 else "bar" end
D>foo :: int -> int | string
D>
А что именно даст статическая типизация? Она будет только для проверки "корректности" кода или еще как-то использоваться в процессе компиляции?
D>Так же можно обсудить, общую здравость идеи зависимости сигнатуры функции от входящих параметров.
Идея интересная. Применительно к динамике я бы еще хотел возможность написать код, вычисляющий тип результата (и выполняющий некоторые проверки) для функции и выполняющийся по каждому метсу вызова. Например, для приведенного мной примера я могу сказать, что именно получится в результате. Более того, все получающиеся преобразования типов в своем коде (на данный момент) я могу описать зная типы исходных аргументов. Если позволить читать внешние метаданные, я могу проверить вообще все типы в программе (часть динамики управляется внешним конфигом в рантайме).
Вообще, сама идея здравая. Но для этого рантайм должен позволять делать нетривиальные вещи. Например, создавать новые типы (классы), вызывать в обобщенной форме функции и т.п. В этом случае "обобщенные" функции могут как-то преобразовывать тип своих аргументов. Что-то вроде недомакросов но при этом в рантайме. Тогда тип результата будет нетривиальным образом зависеть от типов (возможно — и значений) входных параметров. Ну и для проверки корректности, естественно, хотелось бы вычислять типы результатов вызова таких функций (или еще какую-либо информацию).
Re[2]: Анонимные алгебраические типы. Более сложный случай.
M>Эта функция принимает функцию (с произвольным числом и типом аргументов, более точной типизации функций в языке нет) и возвращает функцию, которая добавляет дополнительный уровень обработки исключений. Для всех возникших в f исключений вызывается exnHandler и исключение пробрасывается дальше. Работает для всех функций — с любым числом и типами аргументов, корректно работает с опциональными параметрами. Вопрос: а как оно будет выглядеть в вашем статически типизированном решении?
Вам тут нужен макрос, а не статическая типизация.
D>>Так же можно обсудить, общую здравость идеи зависимости сигнатуры функции от входящих параметров. M>Идея интересная. Применительно к динамике я бы еще хотел возможность написать код, вычисляющий тип результата (и выполняющий некоторые проверки) для функции и выполняющийся по каждому метсу вызова. Например, для приведенного мной примера я могу сказать, что именно получится в результате. Более того, все получающиеся преобразования типов в своем коде (на данный момент) я могу описать зная типы исходных аргументов. Если позволить читать внешние метаданные, я могу проверить вообще все типы в программе (часть динамики управляется внешним конфигом в рантайме).
M>Вообще, сама идея здравая. Но для этого рантайм должен позволять делать нетривиальные вещи. Например, создавать новые типы (классы), вызывать в обобщенной форме функции и т.п. В этом случае "обобщенные" функции могут как-то преобразовывать тип своих аргументов. Что-то вроде недомакросов но при этом в рантайме. Тогда тип результата будет нетривиальным образом зависеть от типов (возможно — и значений) входных параметров. Ну и для проверки корректности, естественно, хотелось бы вычислять типы результатов вызова таких функций (или еще какую-либо информацию).
Этот поток сознания я не осилил, но похоже вы опять про макросы.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[4]: [Динамик не нужен] Анонимные алгебраические типы.
D>>let x =
D>> match x with
D>> | :? Int as y -> print "is int"
D>> | :? String as y -> print "is string"
D>>
T>Если матчинг обязательный явный, то оно есть уже сегодня, если конструкторы будут названы так же: Int и String. Но я не думаю, что анонимность конструкторов тут самая большая беда. Чтобы воспользоваться значением, его всё-таки придётся из алгебраического типа распаковать — вот что самое неудобное. В динамике "оно само", без лишних switch/match.
В динамике практически то же самое, если со возвращённым значением предполагается делать что-либо посложнее, чем тупо вывести его в строку:
if (is_int($x)) {
exit($x);
} else {
// do something with string $x
}
Re[3]: Анонимные алгебраические типы. Более сложный случай.
Здравствуйте, dotneter, Вы писали:
D>Вам тут нужен макрос, а не статическая типизация.
Зачем это мне здесь нужен макрос? Почему вы решили, что я хочу во время компиляции генерировать какой-то код, а не в рантайме в динамике оборачивать функцию? Тем более что в коде эта функция используется в том контексте, в котором макросы не помогут. Ей на вход кормится страшная функция, собираемая по внешнему конфигу. Исходная функция — interop между всеми модулями приложения и заодно repository, из которого модули получают необходимые им сервисы. Ну а обертка — фактически, одна простая и короткая возможность вывести пользователю "правильные" сообщения об ошибке если что-то пошло не так. Ну и что можно макросом здесь сделать? А вот написать правила преобразования типов этой функцией я вполне мог бы.
Re[4]: Анонимные алгебраические типы. Более сложный случай.
Здравствуйте, maxkar, Вы писали:
M>Здравствуйте, dotneter, Вы писали:
D>>Вам тут нужен макрос, а не статическая типизация.
M>Зачем это мне здесь нужен макрос? Почему вы решили, что я хочу во время компиляции генерировать какой-то код, а не в рантайме в динамике оборачивать функцию?
Я решил что вам нужен статически типизированый врапер для функции, это задача кодогенерации, значит нужен макрос
Я не знаю что у вас там за конструктор страшны функции, но если у вас есть функция
void Foo(){DoSomething()}
То с помощью кодогеренации можно сделать врапер, хоть так
Есть два варианта:
+ Функция, тип которой зависит от [типа] параметров
+ Функция, тип которой зависит от значений параметров
Первый вариант в статике возможен:
printf "%d+%d=%d" 2 2 (2+2)
Второй — нет:
let myPrint fmt = printf fmt ...?
То, что ты приводишь:
def foo(x): if x == 0 then 0 else "bar" end
Судя по твоему описанию абсолютно равносильно:
data Either a b = Left a | Right b
foo x | x == 0 = Left 0
| otherwise = Right "bar"
Тип функции foo при этом — Either. Этот тип вообще никак не зависит от параметров. Аналогичная ф-ция foo в динамике:
let foo x | x == 0 = 0
| else = "bar"
Имеет тип, зависящий от *значения* х. Поэтому код вида:
foo 0 * foo 0
верный и отработает. Но код:
foo 0 * foo 1
неверный и свалится в рантайме.
Системы типов консервативны, они не пытаются доказать правильность программы, они доказывают ее возможную неправильность. Код вида foo x * foo y возможно неправильный (но частный случай foo 0 * foo 0 — правильный). Исходя из того, что выражение foo x * foo y в принципе может быть неправильным, система типов этот код не пропустит.
Re[2]: [Динамик не нужен] Анонимные алгебраические типы.
Здравствуйте, WolfHound, Вы писали:
D>>плюс мне видится это это вполне реализуемое как сахар поверх какого нибудь WH>Вполне себе вариант. WH>правда и сигнатуру функции тогда уж описывать так WH>
WH>foo :: int -> X int | Y string
WH>let foo x = if x == 0 then X 0 else Y "bar" end
WH>
Тип этой функции не зависит от значений параметров. Тип *всегда* int|string.
Re[2]: [Динамик не нужен] Анонимные алгебраические типы.
ВВ>data Either a b = Left a | Right b ВВ>foo x | x == 0 = Left 0 ВВ> | otherwise = Right "bar"
Да, разве что без надобности вводить тип.
ВВ>Тип функции foo при этом — Either. Этот тип вообще никак не зависит от параметров. Аналогичная ф-ция foo в динамике:
Да, но решает туже задачу.
ВВ>let foo x | x == 0 = 0 ВВ> | else = "bar"
ВВ>Имеет тип, зависящий от *значения* х. Поэтому код вида:
ВВ>foo 0 * foo 0
ВВ>верный и отработает. Но код:
ВВ>foo 0 * foo 1
ВВ>неверный и свалится в рантайме.
Суть как раз в том что в статике ничего не свалится, компилятор не даст.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[3]: [Динамик не нужен] Анонимные алгебраические типы.
Здравствуйте, dotneter, Вы писали:
ВВ>>data Either a b = Left a | Right b ВВ>>foo x | x == 0 = Left 0 ВВ>> | otherwise = Right "bar" D>Да, разве что без надобности вводить тип.
Без надобности, да. Вот только если тип не вводить, то все автоматически объявляемые конструкторы будут принадлежать одному типу. И тип функции
foo x = if x == 0 then 0 else"bar"
будет точно такой же, как и у
bar x = if x == 0 then 42.42 else'b'
И спрашивается, чем это вообще отличается от динамики.
Собственно, есть мнение, что динамика — это статика, в которой есть лишь один-единственный тип.
ВВ>>Тип функции foo при этом — Either. Этот тип вообще никак не зависит от параметров. Аналогичная ф-ция foo в динамике: D>Да, но решает туже задачу.
Этот код ты сам придумал, и я знаю, какую задачу он решает. Я приводил другие примеры, и на статике они не выражаются.
ВВ>>неверный и свалится в рантайме. D>Суть как раз в том что в статике ничего не свалится, компилятор не даст.
Да, потому что в статике компилятор просто не скомпилирует эквивалетный код.
Re[5]: [Динамик не нужен] Анонимные алгебраические типы.
Здравствуйте, dimgel, Вы писали:
T>>Если матчинг обязательный явный, то оно есть уже сегодня, если конструкторы будут названы так же: Int и String. Но я не думаю, что анонимность конструкторов тут самая большая беда. Чтобы воспользоваться значением, его всё-таки придётся из алгебраического типа распаковать — вот что самое неудобное. В динамике "оно само", без лишних switch/match.
D>В динамике практически то же самое, если со возвращённым значением предполагается делать что-либо посложнее, чем тупо вывести его в строку:
Нет, не то же самое. В динамике будет утиная типизация.
Re[2]: Анонимные алгебраические типы. Более сложный случай.
M>Эта функция принимает функцию (с произвольным числом и типом аргументов, более точной типизации функций в языке нет) и возвращает функцию, которая добавляет дополнительный уровень обработки исключений. Для всех возникших в f исключений вызывается exnHandler и исключение пробрасывается дальше. Работает для всех функций — с любым числом и типами аргументов, корректно работает с опциональными параметрами. Вопрос: а как оно будет выглядеть в вашем статически типизированном решении?
Если в языке достаточно развитое метапрограммирование будет выглядеть вполне прилично, вот например вариант на шаблонах D:
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, dimgel, Вы писали:
T>>>Если матчинг обязательный явный, то оно есть уже сегодня, если конструкторы будут названы так же: Int и String. Но я не думаю, что анонимность конструкторов тут самая большая беда. Чтобы воспользоваться значением, его всё-таки придётся из алгебраического типа распаковать — вот что самое неудобное. В динамике "оно само", без лишних switch/match.
D>>В динамике практически то же самое, если со возвращённым значением предполагается делать что-либо посложнее, чем тупо вывести его в строку:
ВВ>Нет, не то же самое. В динамике будет утиная типизация.
Чем вас не устраивает структурная?
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[4]: [Динамик не нужен] Анонимные алгебраические типы.
Почему такой же? int|string double|string
ВВ>Этот код ты сам придумал, и я знаю, какую задачу он решает. Я приводил другие примеры, и на статике они не выражаются.
Можете привести пример еще раз, но что бы он не пытался взорвать мозг, и максимально просто отражал то что вы хотите.
ВВ>>>неверный и свалится в рантайме. D>>Суть как раз в том что в статике ничего не свалится, компилятор не даст.
ВВ>Да, потому что в статике компилятор просто не скомпилирует эквивалетный код.
Не скомпилирует он только то что нельзя с полной увереностью сказать сработает при всех возможных сочетаниях типов.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[7]: [Динамик не нужен] Анонимные алгебраические типы.
Т.е. я не смогу написать одну полиморфную функцию, которая будет анализировать результат возвращаемый под видом int|string или double|string?
И кстати, как вообще анализировать тип? Как проверить, что функция вернула строку или число?
ВВ>>Этот код ты сам придумал, и я знаю, какую задачу он решает. Я приводил другие примеры, и на статике они не выражаются. D>Можете привести пример еще раз, но что бы он не пытался взорвать мозг, и максимально просто отражал то что вы хотите.
Скажи, что тебе конкретно взрывает мозг, и я попробую упростить пример именно в этом месте.
Re[8]: [Динамик не нужен] Анонимные алгебраические типы.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, dotneter, Вы писали:
ВВ>>>Нет, не то же самое. В динамике будет утиная типизация. D>>Чем вас не устраивает структурная?
ВВ>Структурная работает только если типы выводятся.
И? Ну и пусть выводятся.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[6]: [Динамик не нужен] Анонимные алгебраические типы.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, dotneter, Вы писали:
ВВ>>>
ВВ>>>bar x = if x == 0 then 42.42 else'b'
ВВ>>>
D>>Почему такой же? int|string double|string
ВВ>Т.е. я не смогу написать одну полиморфную функцию, которая будет анализировать результат возвращаемый под видом int|string или double|string?
Можете :: (Num a) => a|string -> ВВ>И кстати, как вообще анализировать тип? Как проверить, что функция вернула строку или число?
Вы же в той ветке отвечали http://rsdn.ru/forum/philosophy/4251489.1.aspx
или так
bar x :: int ->
bar x :: string ->
ВВ>>>Этот код ты сам придумал, и я знаю, какую задачу он решает. Я приводил другие примеры, и на статике они не выражаются. D>>Можете привести пример еще раз, но что бы он не пытался взорвать мозг, и максимально просто отражал то что вы хотите.
ВВ>Скажи, что тебе конкретно взрывает мозг, и я попробую упростить пример именно в этом месте.
let fun (x::xs) = fun' x xs
where fun' a [] = \y -> y + a
fun' a (x::xs) = \y -> fun' (y + a + x) xs
Обязательно нужно работать со списками, и в обоих случаях возвращать функции, проще ничего не придумать?
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Talk is cheap. Show me the code.
Re[3]: Анонимные алгебраические типы. Более сложный случай.
Здравствуйте, FR, Вы писали:
FR>Если в языке достаточно развитое метапрограммирование будет выглядеть вполне прилично, вот например вариант на шаблонах D:
Да, неплохо. Только вот мне не нравится, что это шаблон. Я хочу именно функцию. Чтобы ее можно было куда-нибудь еще передавать, например. Я не против писать то, что в шаблоне написано, это позволит проверить типы там, где они известны на этапе компиляции. А там, где функция используется на динамике, пусть работает в "моем" варианте. Более того, пусть "статические" вызовы компилятор инлайнит, но динамика все равно остается. Потому что изначально метод предназначался для защиты функции, строящейся по конфигу во время исполнения, а не во время написания кода. Что-то вроде
const cls : Class = findClass(config.confClass);
const instance : Object = new Class();
const func = instance[config.confMethod];
return exceptoinProtect(func, protector);
Более того, на основе этой же функции я могу, например, защищать "интерфейсы":
function protectInterface(iface : Object, protector : Function) : Object {
const res : Object = {};
for (var item : String in iface) {
var val : Object = iface[item];
var valf : Function = val as Function;
res[item] = valf != null ? exceptionProtect(valf, protector) : val;
}
return res;
}
Оно строит новый объект, имеющий те же поля (с теми же значениями) но функции обернуты и защищены. Полезно на инфраструктурном уровне, например, в dependency injection framework. Ну или любой другой внешней конфигурации модулей и межмодульного взаимодействия. На шаблонах оно будет работать только до тех пор, пока известна информация о типах преобразуемых объектов.
Вообще, вся эта магия нужна, когда присутствуют сразу оба типа варианта, описаных Василием. Есть функция, тип результата которой зависит от значения. Такая функция возникает, когда есть какая-либо внешная конфигурация "верхнего" уровня, не выраженная в коде. И при этом есть функции, тип которых зависит от типа аргументов, вроде приведенных выше. В статическом случае их можно реализовать шаблонами/макросами и т.п. Но вот применить их к результату функций первого типа не получится без динамики .
Re[4]: Анонимные алгебраические типы. Более сложный случай.
Здравствуйте, maxkar, Вы писали:
M>Да, неплохо. Только вот мне не нравится, что это шаблон. Я хочу именно функцию. Чтобы ее можно было куда-нибудь еще передавать, например.
Этот шаблон порождает полноценную функцию полностью совпадающую по типу с ее функцией аргументом, с ней можно делать все что можно делать с любой другой функцией. Тут чуть разъясню, шаблоны D в отличии от шаблонов C++ могут порождать любые типы, а не только структуры/классы или функции.
Вот такой код:
то есть типы совершенно одинаковы, от шаблона ничего ни осталось.
M>Я не против писать то, что в шаблоне написано, это позволит проверить типы там, где они известны на этапе компиляции. А там, где функция используется на динамике, пусть работает в "моем" варианте. Более того, пусть "статические" вызовы компилятор инлайнит, но динамика все равно остается. Потому что изначально метод предназначался для защиты функции, строящейся по конфигу во время исполнения, а не во время написания кода. Что-то вроде
В динамике конечно шаблоны бессильны.
.....
M>Более того, на основе этой же функции я могу, например, защищать "интерфейсы":
.....
M>Оно строит новый объект, имеющий те же поля (с теми же значениями) но функции обернуты и защищены.
А это на шаблонах вполне реально.
M>Полезно на инфраструктурном уровне, например, в dependency injection framework. Ну или любой другой внешней конфигурации модулей и межмодульного взаимодействия. На шаблонах оно будет работать только до тех пор, пока известна информация о типах преобразуемых объектов.
Конечно, но тут часто хватает обертки на базовый интерфейс.
M>Вообще, вся эта магия нужна, когда присутствуют сразу оба типа варианта, описаных Василием. Есть функция, тип результата которой зависит от значения. Такая функция возникает, когда есть какая-либо внешная конфигурация "верхнего" уровня, не выраженная в коде. И при этом есть функции, тип которых зависит от типа аргументов, вроде приведенных выше. В статическом случае их можно реализовать шаблонами/макросами и т.п. Но вот применить их к результату функций первого типа не получится без динамики .