"모두를 위한 algebraic effects!" ...정말루?

Posted on Jun 13, 2022

뭐 대충 다음과 같은 글들:

https://www.eff-lang.org/handlers-tutorial.pdf

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/08/algeff-tr-2016-v2.pdf

…그리고 몇 개의 구현체, 포스팅들: (아직은 별루인거 같은데)

https://hackage.haskell.org/package/fused-effects

https://github.com/dry-rb/dry-effects

https://github.com/digital-fabric/affect

https://github.com/macabeus/js-proposal-algebraic-effects

https://github.com/nythrox/effects.js

https://www.janestreet.com/tech-talks/effective-programming/

https://github.com/ocaml-multicore/effects-examples

https://overreacted.io/algebraic-effects-for-the-rest-of-us/

…음… 분명히 한국말으로 번역도 해놓았고, 심지어 js버젼으로 설명/예시도 있는데 나는 전혀 모르겠다 싶었음.

오히려 dry-rb, affect이 더 명확하기는 한거 같아.

가장 실용적으로 접근한 예는, ocaml-multicore에서 활용한 것들 같아 보인다.

분명히 장점을 볼 수 있을거 같은 개념 같다.

왜냐하면,

  1. 지금의 monad을 이용한 효과와 사용처의 분리 방식을 생각해보면,
  2. 하나의 monad context에서는, 한가지 타입의 monad만을 표현가능.
    1. 그래서 여러개의 monad context을 위해 monad transformer 같은것들으로 stacking하여 사용.
    2. (…그때 그때 Haskell do-notation등에 따라 분리해서 표현)
    3. 좋은점이라면 좋은점일수도 있겠지만.
  3. 그런데, aeffects을 이용한다면,
    1. 굳이 그렇게 복잡하게 나누지 않아도 괜찮고,
    2. monad처럼 사용처에서 그 효과의 내용을 분리하기도 좋아 보여.

당연히 그렇기 때문에, 원래의 모나드에서 같는 장점을 그대로 잃지 않으면서, 더 평범하게 적어나가기 좋을거 같다. (…일반적인 imperative programming language에서 I/O/async/await, Maybe등이 동시에 나오거나, …처럼)

그래서, 언어적으로 추가된다면 더 기대할 수 있을 것들이:

  1. Monad이 함수의 타입에 드러나듯이, AEffects도?
    1. 그렇게 된다면, 어떤 함수가 어떤 의존성을 갖는지 이해하기 좋으니까.
    2. 아마, 앞으로 그럴거 같은데?
  2. 그리고, effect handlers들의 composition이 가능할것도 같아.
    1. (페이퍼들에서 내가 제대로 읽지 못한거 같은데,)
    2. 물론, 굳이 그렇게 하지 않아도,
      1. 어차피 사용처에서 복합해 사용한다면,
      2. 그렇게 복합된 것을 적용해줘야 돌아갈거고,
      3. 또 그렇게 복합된 타입을 지정한것을 타입composition으로 표현만 해도 되겠지만.

…ㅎㅎ그런데 어디까지나 이거 정적타입을 위해서, formal하게 정리한것이고, 현실적으로는:

  (define-condition progress ()
    ((amount :initarg :amount :reader amount)))
  
  (defun process-partial-data (data)
    "NOOP placeholder"
    (declare (ignore data)))
  
  (defun process-data (data)
    (restart-case
        (loop
           initially
             (signal 'progress :amount 0)
           with total = (length data)
           for datum in data
           for i below total
           do
             (process-partial-data datum)
             (signal 'progress :amount (/ i total))
           ;; Report progress
           finally
             (signal 'progress :amount 1)
             (return :done))
      (abort-work ()
        (format *trace-output* "Aborting work!")
        :failed)))
        
CL-USER> (handler-bind ((progress (lambda (p)
    (format *trace-output* "~&Progress: ~F~%" (amount p)))))
             (process-data '(1 2 3 4 5 6)))

  Progress: 0.0
  Progress: 0.0
  Progress: 0.16666667
  Progress: 0.33333334
  Progress: 0.5
  Progress: 0.6666667
  Progress: 0.8333333
  Progress: 1.0
  -> :DONE        

…생각해보면, 정말 그냥 커먼리습의 conditions/restarts이지 않은가 싶다.

https://news.ycombinator.com/item?id=20496043 , 리습덕후들 말고도 다른 언어에서도 이렇게 된다고 성토하는 분위기. (…vb은 뭐야 대체, 왜 튀어나온거야…)

…음, 뭐든지 정도가 있어서, 적당히 해야 하는데, 내게 acceptable한 정도는 실은 그냥 커먼리습의 컨디션/리스타트 정도면 acceptable하다.

그리고 CS논문쟁이 아저씨들의 마수에서 조금은 풀려나서, 꿈과 희망으로 가득찰거 같은 미래세계의 환상에서 깨어나 현실을 돌아보니, 그냥 굳이 그러면 모나드 없어도, 어느 정도 cl으로 나는 만족하고 살수있겠구나 싶기도 한, 예상치 못한 훈훈한 교훈과 파랑새를 찾아버린 느낌까지.