amarao: (Default)
[personal profile] amarao
Чем больше я про это думаю, тем больше я его хочу.

Любое замыкание (в расте) может управлять control flow приложения, но только в смысле panic!. Замыкание не может управлять другим control flow, например, break/continue/return.

Это делает не экивалентным цикл итератора и применение map для того же самого итератора. Несправедливо! Если бы только компилятор понимал управление control flow и мог правильно проверить его совместимость...

Date: 2024-05-24 08:28 am (UTC)
From: [personal profile] permeakra
Вот так вот люди и приходят к монадкам и хаскелю.

Date: 2024-05-24 09:01 am (UTC)
From: [personal profile] permeakra
"Монадка" в данном случае имя нарицательное. Вариантов вытащить описание эффектов в типизацию действительно больше одного. Хотя лично мне монадки кажутся наиболее понятными.

Опишите, что он делает.

Date: 2024-05-24 09:53 am (UTC)
From: [personal profile] permeakra
Если я правильно понял, то

module Test where
import Control.Monad

testFn rc bc dv l = either id id $ (forM_ l $ \e -> if rc e then Left e else (if bc e then Left dv else return () ) ) >> return dv


*Test> testFn (==1) (==2) 0 [3,3,2,1]
0
*Test> testFn (==1) (==2) 0 [3,3,1,2]
1
Edited Date: 2024-05-24 09:54 am (UTC)

Date: 2024-05-24 09:54 am (UTC)
From: [personal profile] permeakra
Форматирование съелось. Определение функции в одну строчку.

Date: 2024-05-24 01:31 pm (UTC)
From: [personal profile] permeakra
>Не то, чтобы я хорошо знал хаскели, но выглядит так, что ты просто делаешь энум с вариантами (хоть и замаскированными).

Отчасти. инстанс монадки Either (эквивалент Result в Rust) написан так, что после первого Left ничего не вычисляется, там даже список не будет конструироваться. Кроме того, GHC умный, вполше может промежуточное построение Either optimize away.

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

> Напиши без материализованного энума (без байтов в памяти) и в виде отдельной функции, которую ты вызываешь по имени.

Какие мы привередливые.
import Control.Monad.Cont

testCn :: (a -> Bool) -> (a -> Bool) -> a -> [a] -> a
-- ^ immidiate return condition checker, abort and return default value checker, default value, list to proc.
testCn rc bc dv l = flip runCont id $ callCC $ \rexit -> do
  callCC $ \lexit -> forM_ l $ \e -> do
     when (rc e) $ rexit e
     when (bc e) $ lexit ()
  return dv

Используется монада продолжений, на которой можно сочинить любой разумный control flow. Первый callCC дает продолжение rexit (return-exit), которое позволяет выскочить из функции сразу, второй callCC дает продолжение, которое выскакивает перед
 return dv 
Edited Date: 2024-05-24 01:32 pm (UTC)

Date: 2024-05-27 10:04 am (UTC)
From: [personal profile] permeakra
Аннотировал скобками и точками-с-запятой. Оба when, понятное дело, во внутренней лямбде.
testCn rc bc dv l = flip runCont id $ callCC $ \rexit -> do {
  callCC $ \lexit -> forM_ l $ \e -> do{
     when (rc e) $ rexit e;
     when (bc e) $ lexit ();
  };
  return dv;
  }


runCont - раскрывает монадку продолжения
callCC - расставляет точки возврата (куда выходить) и отправляет в свой аргуменет условную 'команду выхода' в эту точку возврата.
forM_ - запустить монадические вычисления на 'iterable' коллекции, и склеить 'слева направо', выкидывая результат. Он получает здесь лямбду, в которую передает элемент. Эта внутренняя лямбда синтаксически захватывает 'команды выхода' из более внешних.

Если тебя смущает $, то это чтоб скобки не размножались. f $ g == f (g) . Удобно, когда нужно отграничить лямбду или сделать длинный пайплайн.
Edited Date: 2024-05-27 03:57 pm (UTC)

Date: 2024-05-24 04:27 pm (UTC)
yurikhan: (Default)
From: [personal profile] yurikhan

Семантика цикла в верхнем примере — поиск с ранним выходом при успехе и ранним осознанием облома. Соответственно, его надо выражать не в базисе map/filter, а в базисе map/filter/take 1.

Не тестировал, но что-то такое:

NotFound = object()
Break = object()
Continue = object()

def find(xs, sample) -> TypeFoo:
    z = next((y
              for x in xs
              if Continue != (y := (x.make_foo(sample) if x.should_return(sample) else
                                    Break if x.should_stop(sample) else
                                    Continue))),
             NotFound)
    return {NotFound: TypeFoo, Break: TypeFoo}.get(z, lambda: z)()

Date: 2024-05-24 12:30 pm (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi

Не обязательно к хаскелю, э. Скала тоже хорошая.

Date: 2024-05-24 01:31 pm (UTC)
From: [personal profile] permeakra
В скалу завезли TCO ?

Date: 2024-05-24 01:56 pm (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi

Ну где-то да. At a certain degree.

Date: 2024-05-25 05:25 am (UTC)
yurikhan: (Default)
From: [personal profile] yurikhan

Вообще мысли вида «хочу иметь break/continue/return expr/throw expr в виде first-class значения» обычно приходят при попытке применить рефакторинг Extract Method к куску говнокода, где на входе коллекция, потом по ней for выше человеческого роста, а внутри этого for’а — вышеупомянутые управляющие конструкции в ассортименте.

Date: 2024-05-27 10:04 am (UTC)
yurikhan: (Default)
From: [personal profile] yurikhan

Каждое i должно быть обработано каждой из 10000 функций KYC/AML подразделения

А характерный размер коллекции (из которой каждое i)?

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

Date: 2024-05-27 10:07 am (UTC)
yurikhan: (Default)
From: [personal profile] yurikhan

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

Date: 2024-05-29 12:15 pm (UTC)
yurikhan: (Default)
From: [personal profile] yurikhan

Ну и? Надо сесть за моделирование и тип возврата этих функций-требований сформулировать в терминах предметной области («этому разрешить без очереди и без дальнейших проверок», «да, если не противоречит другим проверкам», «минус десять очков Гриффиндору», «ни за что и никогда»), а не в терминах управляющих конструкций того цикла, в котором перебираются проверки.

Если проверки одного клиента влияют на результаты проверок для других (например, у нас ограниченный ресурс), то, значит, у нас опять не задача на map-map-filter, а на map-map-sum-sort-take 100.

Profile

amarao: (Default)
amarao

February 2026

S M T W T F S
123456 7
8910111213 14
15161718192021
22232425262728

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 25th, 2026 11:53 am
Powered by Dreamwidth Studios