LINUX.ORG.RU

Замыкание как аргумент другого замыкания

 


0

4

Как в Rust можно определить функцию, аргументом которой является замыкание с аргументом-замыканием?

Вариант с динамической диспетчеризацией работает, но со статической компилятор сообщает об ошибке «the type of this value must be known in this context»:

fn dyn1(f: &Fn(i32)) {
}

fn dyn2(f: &Fn(&Fn(i32))) {
}

fn stat1<F: Fn(i32)>(f: F) {
}

// ???
fn stat2<F: Fn(i32), F2: Fn(F)>(f: F2) {
}

fn main() {
    // x - целое число, динамическая диспетчеризация
    dyn1(&|x| { });

    // fx - замыкание с целым аргументом, динамическая диспетчеризация
    dyn2(&|fx| {fx(1)});

    // x - целое число, статическая диспетчеризация
    stat1(|x| { });

    // fx - замыкание с целым аргументом, статическая диспетчеризация
    stat2(|fx| {fx(1)}); // ошибка: the type of this value must be known in this context
}
★★

Судя по всему никак. Дело в том, что Fn это трейт, а не конкретный тип. Соответственно в строке

stat2(|fx| {fx(1)});
функция stat2 полиморфна по типу принимаемой функции и компилятор не может вывести тип передаваемой в аргументы замыканию функции в во время компиляции.

Nexmean
()

Но вообще тут можно написать вот так

fn dyn3<F: Fn(&Fn(i32))>(f: F) {
}

fn main() {
    dyn3(|fx| {fx(1)});
}
ибо в твоём коде компилятор статически не может вывести тип только передаваемой в замыкание функции
stat2(|fx| {fx(1)});
//     ^^
//    здесь, неизвестен тип во время компиляции, известен только
//    where F: Fn(i32) - трейт

Nexmean
()
Ответ на: комментарий от Nexmean

Да, так работает, почему-то я пропустил такой способ. Спасибо.

No ★★
() автор топика
Ответ на: комментарий от ckotinko

Тоесть тебе вот так больше нравится?

#include <functional>

void dyn1(std::function<void(int32_t)> f) {
}

void dyn2(std::function<void(std::function<void(int32_t)>)> f) {
}

template <typename F>
void stat1(F f) {
}

template <typename F, typename F2>
void stat2(F2 f) {
}

int main() {
    dyn1([](auto x) { });
    dyn2([](auto fx) {fx(1);});
    stat1([](auto x) { });
    stat2([](auto fx) {fx(1);});
}

pftBest ★★★★
()

Ничего не получится, видимо. Если переписать stat2 как:

fn stat2<F: Fn(Fn(i32))>(f: F) {}
то получаем:
Playpen Error:    Compiling playground v0.0.1 (file:///playground)
error[E0277]: the trait bound `std::ops::Fn(i32) + 'static: std::marker::Sized` is not satisfied in `(std::ops::Fn(i32) + 'static,)`
--> src/main.rs:8:1
  |
8 | fn stat2<F2: Fn(Fn(i32))>(f: F2) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::ops::Fn(i32) + 'static` does not have a constant size known at compile-time
  |
  = help: within `(std::ops::Fn(i32) + 'static,)`, the trait `std::marker::Sized` is not implemented for `std::ops::Fn(i32) + 'static`
  = note: required because it appears within the type `(std::ops::Fn(i32) + 'static,)`
  = note: required by `std::ops::Fn`
error: aborting due to previous error
error: Could not compile `playground`.
To learn more, run the command again with --verbose.

Virtuos86 ★★★★★
()
Ответ на: комментарий от ckotinko

Ну покажите мне аналог:

fn main() {
    let mut list = vec![1, 2, 3];
    if let Some(idx) = list.iter().position(|v| *v % 2 == 0) {
        list[idx] *= 4;
    }
    
    println!("{:?}", list); // [1, 8, 3]
}

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Плохой пример, тут лямбды не нужны:

fn main() {
    let mut list = vec![1, 2, 3];
    for i in 0..list.len() {
        if list[i] % 2 == 0 {
            list[i] *= 4;
            break;
        }
    }
    println!("{:?}", list); // [1, 8, 3]
}

И код намного понятнее, и ассемблер на порядок лучше: https://godbolt.org/g/qoR8rf

pftBest ★★★★
()
Ответ на: комментарий от pftBest

И код намного понятнее

Вкусовщина с кучей способов выстрелить себе в ногу.

RazrFalcon ★★★★★
()
Ответ на: комментарий от pftBest

Ну и поинт в том, что это:

let mut list = vec![1, 2, 3];
if let Some(idx) = list.iter().position(|v| *v % 2 == 0) {
    list[idx] *= 4;
}
лучше чем это:
std::vector<int> list = { 1, 2, 3 };
auto i = std::find_if(list.begin(), list.end(), [](const int v){ return v % 2 == 0; });
if (i != list.end()) {
    *i *= 4;
}

RazrFalcon ★★★★★
()
Ответ на: комментарий от vzzo

неужели такая запись

function<Baz(Foo, Bar)>
настолько лучше этой
&Fn(Foo, Bar) -> Baz
что разница на порядок?

А в последних двух функциях я не смог придумать как передать весь смысл (наверное нужны концепты), так что их по-хорошему не корректно сравнивать.

pftBest ★★★★
()
Ответ на: комментарий от NextGenenration

На с++ картины аски-артом пишут компилящиеся и работающие. Но на нем можно писать красиво без закидонов. А на расте мешанина скобок и пунктуации

ckotinko ☆☆☆
()
Ответ на: комментарий от pftBest

Зачем ты пытаешься врать?

[code=] template<typename ... args> using Fn = std::function<void(args...)>;

void dyn1(Fn<int32_t> f) { }

Зачем ты пытаешься убогий колхозный днищесахар выдавать за что-то?

Далее, зачем ты пытаешься убогую пасту из жабки, в которой переменные берутся магией - выдавать за что-то? Это полный аутизм. Для колхозников ведь не нужно передавать через «шаблоны» не типы.

А так да, в 10раз лучше.

anonymous
()
Ответ на: комментарий от RazrFalcon

Ещё один колхозник, который бегает везде с 2-3 примерами днищенского сахара уровня помойки, что-то доказывая.

В данной потуги колхозник бегает с position - абсолютно бесполезной хернёй.

Да и мало того, клоун даже крестов не знает. Вектор там не нужен - достаточно auto. Как и смысла в этой потуги - ровно ноль.

И да, как всегда колхозник не может писать код:


  std::vector list{1, 2, 3};
  for(auto &x: list) if(x % 2 == 0) {x *= 4; break;};

Клон, не повезло тебе.

anonymous
()
Ответ на: комментарий от tailgunner

А я только решил зайти и почитать. Просто жопу на куски порвало с это невероятно ахинеи. Ещё кот рак и не может вас множить на ноль, но я не кот.

anonymous
()
Ответ на: комментарий от anonymous

То что в плюсах можно наделать себе сахара, не значит что иметь сахар из коробки плохо.

убогую пасту из жабки

Это ты про capture list? В расте не получится случайно захватить ссылку на стек, так что прострелить себе ногу сложнее.

pftBest ★★★★
()
Ответ на: комментарий от pftBest

настолько лучше этой

Я уже тебе объяснял разницу. Разница в том, что в твоём языке нет синтаксиса, не какой-либо логики. Это рандомные символ на которые навешена рандомная семантика.

Ты никогда не не объяснить ахинею с ||, ведь это просто рандомные символы. Это не [], который обладают общей семантикой «массив», это не (), который обладают чёткой семантикой - вызов. Они описывают и в рамках типа, и используются при вызове.

Да, ты можешь напасти всего отовсюду, выдав это за новый язык. Но он выглядит как дерьмо, да и почему как.

Ну и & - это просто ахтунг.

anonymous
()
Ответ на: комментарий от pftBest

То что в плюсах можно наделать себе сахара, не значит что иметь сахар из коробки плохо.

Нет, у тебя нет языка. У тебя есть 2-3 сахарные рандомные конструкций, которые сектанты пытаются тулить за фишки. Пример выше.

Этот сахар работает в 2-3 кейсах, если даже 2-3 есть, а в остальных - это полный рандомный ахтунг. Сложность любого интерфейса в том, чтобы не просто всё дерьмо скидать в кучу, выдав это за язык. А привести это всё к нормальному, общему виду.

Это ты про capture list?

Это я про спащенные из жабки «генерики».

В расте не получится случайно захватить ссылку на стек, так что прострелить себе ногу сложнее.

Из этого мало что следует. А в жабке ещё сложнее, дальше что?

anonymous
()
Ответ на: комментарий от pftBest

А ты, оказывается, написал уже то же, что написал я. Молодец, в мире растоманов не всё потеряно.

Хотя в очередной раз мы видим это точечное убожество, убогие счётчики, убогие == и прочее.

anonymous
()
Ответ на: комментарий от RazrFalcon

Это клоунада меня удивляет. Клоуны спастили из какой-то фп-параши однострочные лямбды. Во-первых - этими лямбдами можно только подтереться - ни для чего вмеянемого они неприменимы, только хелвордами убогими пугать домохозяек.

И что самое интересное - тут во все поля прослеживается идиотия. Пациенты не дают людям писать циклы, ифы и прочее без скобок, но лямбды дают. Почему? потому что клоуны. Нужно было прочно пастить лямбды «не как в С++», хотя они ничем не отличаются. Ради этого мы подотрёмся всем тем, что орали раньше.

Ради этого - клоуны даже из функций return выкинули и повесили семантику на строчку без ;. Это настолько днище, что просто нет слов.

Вся суть. Единственное что они смогли - это, судя по всему, запихнуть аргументы в ||, т.е. в [] у крестов.

Т.е. клоуны сделали из лямбды выражение, хотя лямбда - это не выражение. Но зачем это. Главное хелворды везде пастить. Для идиотов прокатит.

anonymous
()
Ответ на: комментарий от anonymous

А привести это всё к нормальному, общему виду.

Так это, если ты так хорошо знаешь как должно все быть, почему не сделал еще свой язык? Вот кот например уже делает, а ты отстаешь.

Из этого мало что следует. А в жабке ещё сложнее, дальше что?

Из этого следует что нет такой острой необходимости иметь capture list.

Ну и & - это просто ахтунг.

Как раз тут все логично и последовательно. За трейтом Fn скрывается анонимная стуктура, в которой лежат собственно все захваченные данные. Так как размер этой структуры может быть разный, то ее нельзя передать по значению в функцию которая не знает ее тип. Но можно по указателю.

А вот что лежит внутри std::function хрен пойми, скорее всего указатель на кучу + оптимизация когда данных мало.

перепиши это на раст.

Только encode или весь пример? весь наверное лениво будет

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Так это, если ты так хорошо знаешь как должно все быть, почему не сделал еще свой язык?

Это так не работает. Ведь вы выдаёте это за язык, а я логично, что могу спрашивать с него? Как и с вас, ведь вы же с крестов спрашиваете?

На самом деле я тут недавно читал кота - у него есть некоторые схожие идеи с моими. Но меня не устраивает дерьмо такое, какое бы устроило кота. Занят на заводе 24/7.

Из этого следует что нет такой острой необходимости иметь capture list.

У вас очень всё удобно работает. Всё, что есть - хорошо. А чего нет - ну оно и ненужно.

Как раз тут все логично и последовательно. За трейтом Fn скрывается анонимная стуктура, в которой лежат собственно все захваченные данные.

И?

Так как размер этой структуры может быть разный, то ее нельзя передать по значению в функцию которая не знает ее тип. Но можно по указателю.

Во-первых. Почему же в рамках статического языка мы не знаем типа полей? Это выглядит странно.

Второе - зачем указатели, если есть ссылки? Зачем вообще определять тип, который не может быть определён? Это ведь не имеет смысла.

Если мы не можем ничего описать этим типом - зачем он вообще нужен?

Только encode или весь пример? весь наверное лениво будет

Нет. Возьми то растовое поделие и перепиши там encode, если не лень.

anonymous
()
Ответ на: комментарий от anonymous

Почему же в рамках статического языка мы не знаем типа полей?

Потому что логика. У тебя есть лямбда типа А и вторая лямбда типа Б. Ты не можешь в одну единственную функцию передать по значению и А и Б, потому что она не будет знать какие деструкторы ей вызвать.

У тебя только два варианта: или сделать функцию шаблонной (тоесть сделать две функции, одну для А вторую для Б) или передать данные по указателю.

Можно еще конечно засунуть в сами данные их размер и указатель на деструктор, но это уже совсем наркомания будет, на бекэндах не оценят.

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Потому что логика.

Это не логика. Это просто то, о чём я уже сотни раз говорил. Колхозники не могут сделать нормальный язык, даже пародию на него. Это хелворд с рантаймом уровня жабка 95года.

Потому что логика. У тебя есть лямбда типа А и вторая лямбда типа Б.

И?

Ты не можешь в одну единственную функцию передать по значению и А и Б, потому что она не будет знать какие деструкторы ей вызвать.

Могу, я в 80х что-ли, где подобное решалось через динамику уровня рантайма?

Хотя о чём я говорю - это же колхозники с недоязыком, естественно, что только в динамику уровня рантайма они и могут, ведь иначе - сложно.

Один «компилтайм принтф» на vtable чего стоит. Меня до сих пор смех не отпускает.

У тебя только два варианта: или сделать функцию шаблонной (тоесть сделать две функции, одну для А вторую для Б)

Естественно, что нормальное решение - генерик-функция.

Можно еще конечно засунуть в сами данные их размер и указатель на деструктор, но это уже совсем наркомания будет, на бекэндах не оценят.

Именно так этот колхоз и реализован. Если ты передаёшь указатель, то должен быть враппер, который всё это дело сведёт к единому виду.

Т.е. если ты знаешь тип, и у тебя статический вызов - ты можешь заранее подготовить контекст для лямбды, а когда у тебя всё проходит через указатель - не можешь.

Хотя у колхозников как всегда - пытаются получить халявку от крестов с ихней ллвм.

anonymous
()
Ответ на: комментарий от pftBest

В конечном итоге - я хаял кресты уже много лет за то, что там дерьмо вместо функцию. И что я вижу? Супер-язык ещё более убогий, хотя я понимаю почему это. Написать рантайм-дерьмо и скормить его llvm - это потолок для тех колхозников, что пишут эту поделку.

Сделай ты прозрачную лямбду, прозрачную передачу типов, метаинфы, рефлексию. А что мы видим? Указатель. Ладно крестам это простительно, но новому «супер»-«языку»? Нет.

Ты не ответил, но я всё же повторю. Зачем существует тип, который Fn(int32), если он неюзабельный?

В сишке существуют типы-функции, которые нельзя использовать - только как ссылки на глобалы. И то - подобное решение крайне спорно. Как и decay. Но это всё можно понять - это логично, это был 70год.

Но почему то же самое в твоём супер-языке? Что за нахрен? Почему я не могу использовать тип-функцию как человек? Это просто ужас.

И я человек, который очень критично относится к крестам - это просто дерьмо дерьма. У меня жопа горит из-за невероятного кол-ва кастылей и бесполезности в нём. И казалось бы - я должен быть рад новым-супер языкам. Я хочу нормальный язык, но.

Что мне предлагает эта поделка? Это полное убожество. Кроме ретрансляции упоротых агиток от школьников - я ничего не вижу. Это я такой идиот, либо вы? Либо у вас совершенно другие критерии, нежели у меня.

Я не понимаю.

anonymous
()
Ответ на: комментарий от anonymous

Ну почему ты даже не пытаешься прочитать внимательно что я тебе пишу?

Колхозники не могут сделать нормальный язык

Вот видишь что ты делаешь? Началось все с того что ты спросил почему в динамической диспетчиризации нужен указатель, я тебе ответил. А ты теперь орешь что раст говно и шаблонных функций не существует. Ты читал хотябы шапку треда в котором ты пишешь? Ты видишь разницу между первой функцией (dyn1) и третьей (stat1)?

Конечно в расте можно и так и так передать. У тебя есть выбор. Хочешь динамику получи vtable. Не хочешь используй шаблоны. Плюс в отличие от плюсов, у тебя есть выбор где будут данные лежать на стеке или на куче.

Именно так этот колхоз и реализован. Если ты передаёшь указатель, то должен быть враппер, который всё это дело сведёт к единому виду.

А тут я тебе совершенно про другое писал, неужели так не понятно было? Я имел в виду что единственный способ передать «по значению» и без шаблонов, это varargs 2.0, но разработчики бекэнда не оценят.

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Вот видишь что ты делаешь? Началось все с того что ты спросил почему в динамической диспетчиризации нужен указатель, я тебе ответил. А ты теперь орешь что раст говно и шаблонных функций не существует. Ты читал хотябы шапку треда в котором ты пишешь? Ты видишь разницу между первой функцией (dyn1) и третьей (stat1)?

Неверно. Шаблонных функций там действительно нет, а про спащенные из жабки генерики - я уже говорил. Т.е. это я знаю.

Другое дело, что у пацана это не работает, а предложили ему решение без шаблонов. Хотя этому ничего не должно мешать.

При этом - шаблоны в том виде, в котором их( в расте) вижу я - это убожество. Я ведь дальше разбирал эту тему.

Функция должна по умолчанию быть статической, все аргументы - должны быть статическими. Не с прикастыленным сверху генериками спащенными из жабки.

Я писал именно о прозрачных «шаблонах», а не убожество из 80годов.

Плюс в отличие от плюсов, у тебя есть выбор где будут данные лежать на стеке или на куче.

Сомневаюсь. Раст - это колхоз поверх ллвм, а значит - сомневаюсь, что там ты можешь как значение передать объекты разного размера в функцию. Потенциально, ты можешь кастыль с аллокой и ллвм это даёт нахаляву, т.к. возможно эти колхозники туда это забабахали. Но это потолок.

А тут я тебе совершенно про другое писал, неужели так не понятно было? Я имел в виду что единственный способ передать «по значению» и без шаблонов, это varargs 2.0, но разработчики бекэнда не оценят.

Нет - есть ещё третий путь. Это нормальные функции, которые и должны существовать в настоящие время, а не это убожество из 80года.

Поясню. Существование на уровне рантайма такого понятия как «функция» в 95% случаев - протухло. Нет смысла выражать функции через рантайм.

Функция должна быть неким чисто инлайновым компилтайм интерфейсом. Его можно с вести и до вызова функции, хотя это почти никогда не нужно.

Если проще. Сейчас в статическом языке существует статический компилтайм, хотя он не нужен. Не обязательно компилтайму быть статическим - он должен быть динамический, ведь компилятору поклать - он итак всё знает.

Т.е. в твоей ситуации, где у тебя функция не знает ничего о типах - она должна знать. В твоём недоязыке реализованы переменные как простое именование объектов - это и есть та часть динамического языка.

У тебя все переменные - есть динамические привязки, но которые существуют в рантайме. Тем же самым может быть и функция. Все аргументы - являются такими же привязками. Они могут быть ограничены интерфейсами. Прямо, либо косвенно.

И если я пишу f(void a(int)) - это функция, где аргумент - есть лишь описание интерфейса. Он может быть описан неявно - семантикой функции. f(auto a) {a.call();} И это не сахар шаблонный, как в лямбдах.

Работает это просто как эксперт переменных в область видимости блока, а блок вставляется по месту вызова. В большинстве случаев.

Я уже много лет назад рассказывал про это. Можно и так - f.a = 10; f();

anonymous
()
Ответ на: комментарий от anonymous

Другое дело, что у пацана это не работает, а предложили ему решение без шаблонов. Хотя этому ничего не должно мешать.

Тут проблема совсем простая, правильный ответ в третьем коментарии. У функции два дженерик параметра, а он дал ей только один. Попробуй написать такое в плюсах, увидишь.

Функция должна по умолчанию быть статической, все аргументы - должны быть статическими. Не с прикастыленным сверху генериками спащенными из жабки.

Я не знаю где ты тут джаву нашел, там ведь дженерики все в рантайме. В расте шаблоны раскрываются как в плюсах, трейты участвуют только в компайлтайме чтобы не разрешить пользователю передать неправильные типы. В плюсах тоже делают похожую фичу, концепты называется.

Сомневаюсь.

Я тут про другое говорил. В плюсах std::function требует аллокатор, а в расте нет. Плюс в этом году стабилизируют impl trait а это значит можно будет возвращать лямбды из функции без выделения на куче.

Т.е. в твоей ситуации, где у тебя функция не знает ничего о типах - она должна знать.

Ты предлагаешь разрешить вывод типов за границы функции? Есть уже один такой язык, хаскель называется. Результат будет совсем нечитаем.

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Тут проблема совсем простая, правильный ответ в третьем коментарии. У функции два дженерик параметра, а он дал ей только один. Попробуй написать такое в плюсах, увидишь.

template<typename F = void(int32_t), typename F2 = void(F)> void stat2(F2) {}

В чём проблема? Второй тип выводится из первого. И он тут сам указывает тип функции, хотя это не имеет смысла.

Я не знаю где ты тут джаву нашел, там ведь дженерики все в рантайме.

Понятия не имею где там эти джинерики, но это 1в1 спащено из жабки.

В плюсах тоже делают похожую фичу, концепты называется.

Трейты - это колхозные интерфейсы, а концепты более «широкая» фича.

Я тут про другое говорил. В плюсах std::function требует аллокатор, а в расте нет.

Сомневаюсь. Где у тебя будет храниться контекст лямбды? В астрале?

Плюс в этом году стабилизируют impl trait а это значит можно будет возвращать лямбды из функции без выделения на куче.

Да неужели. Я тебя удивлю - в крестах это было ещё тогда, когда раст не родился.

Ты предлагаешь разрешить вывод типов за границы функции?

Я предлагаю вообще отказаться от такого убого понятия, как функция. Функция - это просто - передача исполнения к блок с контекстом.

Есть уже один такой язык, хаскель называется. Результат будет совсем нечитаем.

Раст не далеко ушел.

Повторю ещё раз. Понятие «функция» не нужно. Никаких границ функций - нет. Функция становится чисто компилтайм понятием без рантайм ограничений. Т.е. функция становиться полноценным типом, таким же как структура.

В крестах уже есть пародия на нормальную функцию - это operator() с контекстом.

anonymous
()
Ответ на: комментарий от anonymous

Зачем существует тип, который Fn(int32), если он неюзабельный?

Вы бы хоть немного вникли в мои слова в первых постах и глупые вопросы бы не стали задавать. Fn(int32) это не тип, это класс типов, множество типов реализующих интерфейс Fn(int32).

Nexmean
()
Ответ на: комментарий от Nexmean

Вы бы хоть немного вникли в мои слова в первых постах и глупые вопросы бы не стали задавать.

Меня мало интересует твоя религия.

Fn(int32) это не тип, это класс типов, множество типов реализующих интерфейс Fn(int32).

Точно так же мне неважно, как и что в твоей религии называется. Я об этом не спрашивал.

Если этот класс типов описывает все возможные типы, то что оно у тебя не работает? Если он не может принять все возможные типы - это мусор и бесполезность. Это именно то, о чём я говорил. А как у вас там это называется - меня не волнует и волновать не должно.

anonymous
()
Ответ на: комментарий от anonymous

Функция - это просто - передача исполнения к блок с контекстом.

В расте это записывается так

fn foo<T, U, F>(f: F, v: T) -> U
where F: Fn(T) -> U
{ 
    f(v) 
}

`f` - это контекст плюс блок кода. `f(v)` - передача управления к блоку кода с контекстом. Что не нравится?

red75prim ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.