(기능적) 반응 형 프로그래밍이란 무엇입니까?


1148

반응 형 프로그래밍 에 관한 Wikipedia 기사를 읽었습니다 . 또한 기능적 반응성 프로그래밍 에 관한 작은 기사를 읽었습니다 . 설명은 매우 추상적입니다.

  1. FRP (기능적 반응성 프로그래밍)는 실제로 무엇을 의미합니까?
  2. 반응 형 프로그래밍 (비 반응 형 프로그래밍과 달리)은 무엇으로 구성됩니까?

저의 배경은 명령형 / OO 언어로되어 있으므로이 패러다임과 관련된 설명을 부탁드립니다.


159
활발한 상상력과 훌륭한 스토리 텔링 기술을 가진 사람이 여기에 있습니다. paulstovell.com/reactive-programming
melaos

39
누군가는 우리 모두가 자동 조정을 위해 "모두에 대한 기능적 반응성 프로그래밍"을 작성해야합니다. 내가 찾은 모든 자원, 심지어 Elm조차도 지난 5 년 동안 CS에서 석사를 받았다고 가정합니다. FRP에 대해 잘 아는 사람들은 순진한 관점에서 문제를 볼 수있는 능력을 완전히 잃어버린 것 같습니다. 가르침, 훈련 및 복음 전파에 중요한 것입니다.
TechZen

26
또 다른 훌륭한 FRP 소개 : 내 동료 인 André
Jonik

5
내가 본 것 중 가장 좋은 것 중 하나, 예 기반 : gist.github.com/staltz/868e7e9bc2a7b8c1f754
Razmig

2
스프레드 시트 비유가 첫 번째 거친 인상으로 매우 유용하다는 것을 알았습니다 (Bob의 답변 : stackoverflow.com/a/1033066/1593924 참조 ). 스프레드 시트 셀은 다른 셀 (풀)의 변경에 반응하지만 다른 셀에 도달하거나 변경하지 않습니다 (누르지 않음). 결과적으로 하나의 셀을 변경할 수 있고 다른 셀은 자체적으로 디스플레이를 '독립적으로'업데이트 할 수 있습니다.
Jon Coombs 1

답변:


931

FRP에 대한 느낌을 원한다면 1998 년 의 오래된 Fran 튜토리얼 ( 애니메이션 일러스트레이션)을 시작할 수 있습니다. 논문의 경우 Functional Reactive Animation 으로 시작한 다음 내 홈페이지의 발행물 링크 및 Haskell WikiFRP 링크에 대한 링크를 확인하십시오 .

개인적으로, FRP가 어떻게 구현 될 수 있는지를 다루기 전에 FRP의 의미 에 대해 생각하고 싶습니다 . (사양이없는 코드는 질문이없는 답변이므로 "잘못된 것"도 아닙니다.) 따라서 Thomas K가 다른 답변 (그래프, 노드, 가장자리, 발사, 실행, 기타). 가능한 구현 스타일은 많지만 FRP 무엇인지 구현하는 것은 없습니다 .

나는 FRP가 " '시간이 지남에 따라 값을 나타내는 데이터 유형"에 관한 Laurence G의 간단한 설명과 공감합니다. 기존의 명령형 프로그래밍은 상태 및 돌연변이를 통해 이러한 동적 값을 간접적으로 만 캡처합니다. 완전한 역사 (과거, 현재, 미래)에는 일등석 표현이 없습니다. 또한 명령형 패러다임이 일시적으로 분리되기 때문에 이산 적으로 진화하는 값만 (간접적으로) 캡처 할 수 있습니다. 반대로, FRP는 이러한 진화하는 값을 직접 캡처 하며 지속적으로 진화하는 값 에는 어려움이 없습니다 .

FRP는 또한 명령형 동시성 (imperative concurrency)을 괴롭히는 이론적 및 실용적 쥐의 둥지를 무시하지 않고 동시에 진행된다는 점에서 특이합니다. 의미 상 FRP의 동시성은 세밀 하고 결정적 이며 연속적 입니다. (구현이 아닌 의미에 대해 이야기하고 있습니다. 구현에는 동시성 또는 병렬 처리가 포함될 수도 있고 포함되지 않을 수도 있습니다.) 의미 적 결정은 엄격하고 비공식적 인 추론에 매우 중요합니다. 동시성은 비 결정적 인터리빙으로 인해 명령형 프로그래밍에 엄청난 복잡성을 추가하지만 FRP에서는 쉽지 않습니다.

FRP 란 무엇입니까? 직접 발명했을 수도 있습니다. 다음 아이디어로 시작하십시오.

  • 동적 / 진화하는 값 (즉, "시간이 지남에 따른 값")은 그 자체로 일류 값입니다. 그것들을 정의하고 결합하여 기능으로 전달하거나 전달할 수 있습니다. 나는 이것을 "행동"이라고 불렀다.

  • 동작은 일정한 (정적) 동작 및 시간 (시계와 같은)과 같은 몇 가지 기본 요소로 구성된 다음 순차적 및 병렬 조합으로 구성됩니다. n 동작은 "정적"인 n-ary 함수 (정적 값)를 적용하여 시간이 지남에 따라 지속적으로 결합됩니다.

  • 불연속 현상을 설명하기 위해, 다른 유형 (가족)의 "이벤트"가 있으며, 각 유형에는 스트림 (유한 또는 무한)이 있습니다. 각 발생에는 관련 시간과 값이 있습니다.

  • 모든 행동과 사건을 구성 할 수있는 구성 어휘를 생각해 내려면 몇 가지 예를 들어보십시오. 보다 일반적인 / 간단한 조각으로 해체하십시오.

  • 당신이 탄탄한 기초에 있다는 것을 알기 위해, denatotional semantics의 기술을 사용하여 전체 모델에 구성 기초를 제공하십시오. 이는 단지 (a) 각 유형에 해당하는 단순하고 정확한 수학적 유형의 "의미"가 있고, b) 각 기본 요소와 연산자는 구성 요소의 의미에 따라 단순하고 정확한 의미를 갖습니다. 절대로 구현 고려 사항을 탐색 프로세스에 혼합 하지 마십시오 . 이 설명이 이해하기 어려운 경우 (a) 유형 클래스 형태의 Denotational 디자인 , (b) Push-Pull 기능 반응 프로그래밍 (구현 비트 무시) 및 (c) Dentational Semantics Haskell Wikibooks 페이지를 참조하십시오.. 변형 적 의미론은 두 명의 설립자 Christopher Strachey와 Dana Scott의 두 부분으로 구성되어 있습니다. 더 쉽고 유용한 Strachey 부분과 더 어렵고 덜 유용한 (소프트웨어 디자인) Scott 부분입니다.

이러한 원칙을 고수하면 FRP의 정신으로 뭔가를 얻을 것으로 기대합니다.

이 원칙들을 어디서 얻었습니까? 소프트웨어 디자인에서 나는 항상 같은 질문을한다 : "무엇을 의미 하는가?" Denotational semantics는이 질문에 대한 정확한 프레임 워크를 제공했으며, 운영 또는 공리적 시맨틱과 달리 내 미학에 맞는 하나의 프레임 워크를 제공했습니다. 그래서 나는 행동이 무엇인지 스스로에게 물었다. 나는 명령형 계산의 시간적으로 별개의 특성 이 행동 자체에 대한 자연스러운 설명이 아니라 특정 스타일의 기계에 대한 수용이라는 것을 곧 깨달았습니다 . 제가 생각할 수있는 가장 간단한 행동 설명은 단순히 "연속적인 시간의 함수"입니다. 이것이 바로 저의 모델입니다. 유감스럽게도,이 모델은 쉽고 결정적인 연속성, 결정 성 동시성을 처리합니다.

이 모델을 정확하고 효율적으로 구현하는 것은 상당히 어려운 일이지만 다른 이야기입니다.


78
기능적 반응성 프로그래밍에 대해 알고 있습니다. 그것은 내 자신의 연구 (대화 형 통계 그래픽)와 관련이 있으며 많은 아이디어가 내 작업에 도움이 될 것이라고 확신합니다. 그러나 나는 언어를 빠져 나가는 것이 매우 어렵다는 것을 알았습니다. 실제로 무슨 일이 일어나고 있는지 이해하기 위해 "선명 적 의미론"과 "타입 클래스 형태"에 대해 배워야합니까? 주제에 대한 일반적인 독자 소개는 매우 유용합니다.
hadley 2016 년

212
@Conal : 당신은 당신이 말하는 것을 분명히 알고 있지만, 당신의 언어는 내가 계산 수학에 박사 학위를 가지고 있다고 가정합니다. 나는 시스템 엔지니어링에 대한 배경 지식과 컴퓨터 및 프로그래밍 언어에 대한 20 년 이상의 경험을 가지고 있지만 여전히 귀하의 응답이 당황스럽게 생각합니다. 나는 당신이 영어 ;-)에 귀하의 회신을 다시 게시하는 도전
mindplay.dk

50
@ minplay.dk : 당신의 발언은 당신이 이해하지 못하는 것에 대해 계속 이야기하지 않습니다. 그리고 당신이 찾고있는 특정 영어의 부분 집합에 대해서는 추측하기가 어렵습니다. 그러나 위에 설명 된 내용 중 어떤 부분이 넘어 졌는지 구체적으로 말씀해 주시면 본인과 다른 사람들이 도와 드릴 수 있습니다. 예를 들어, 정의하고 싶은 특정 단어 나 참조를 추가하려는 개념이 있습니까? 나는 글쓰기의 명확성과 접근성을 향상시키는 것을 좋아합니다.

27
"결정"/ "결정"은 올바르게 정의 된 단일 값이 있음을 의미합니다. 대조적으로, 거의 모든 형태의 명령형 동시성은 스케줄러 또는 찾고 있는지 여부에 따라 다른 답변을 줄 수 있으며 교착 상태에 빠질 수도 있습니다. "의미"(보다 의미있는)는 "작동"(대답이 어떻게 계산되거나 얼마나 많은 공간 및 / 또는 시간이 소비 되는가)과 달리 표현 또는 표현의 값 ( "표시")을 나타냅니다. 기계의 종류).
Conal

18
필드에 오랫동안 머물렀다는 것을 자랑 할 수는 없지만 @ mindplay.dk에 동의합니다. 비록 당신이 무슨 말을하는지 아는 것 같았지만, 나는 그것이 무엇을 기대할만큼 망쳐 졌기 때문에 이것이 무엇인지에 대한 빠르고 간단하고 간단한 이해를주지 못했습니다. 이 답변은 주로 첫 번째 질문에 실제로 대답하지 않고 수많은 새로운 질문으로 이끌었습니다. 나는 여전히 현장에서 상대적으로 무지한 경험을 공유하면 얼마나 간단하고 간단하게 필요한지에 대한 통찰력을 얻을 수 있기를 바랍니다. OP와 비슷한 배경에서 왔습니다.
Aske B.

739

순수 기능 프로그래밍에서는 부작용이 없습니다. 많은 유형의 소프트웨어 (예 : 사용자 상호 작용이있는 모든 것)의 부작용에는 일정 수준이 필요합니다.

기능적 스타일을 유지하면서 동작과 같은 부작용을 얻는 한 가지 방법은 기능적 반응성 프로그래밍을 사용하는 것입니다. 이것은 기능 프로그래밍과 반응 형 프로그래밍의 조합입니다. (귀하가 연결 한 Wikipedia 기사는 후자에 관한 것입니다.)

반응 형 프로그래밍의 기본 개념은 "시간이 지남에 따라"값을 나타내는 특정 데이터 유형이 있다는 것입니다. 이러한 변경 시간 값을 포함하는 계산 자체는 시간이 지남에 따라 변경되는 값을 갖습니다.

예를 들어, 마우스 좌표를 시간에 따른 정수 값으로 나타낼 수 있습니다. 우리가 (의사 코드)와 같은 것을 가지고 있다고 가정 해 봅시다.

x = <mouse-x>;
y = <mouse-y>;

언제든지 x와 y는 마우스 좌표를 갖습니다. 비 반응 형 프로그래밍과 달리이 할당은 한 번만 수행하면되며 x 및 y 변수는 자동으로 "최신"상태를 유지합니다. 반응 형 프로그래밍과 기능적 프로그래밍이 함께 잘 작동하는 이유는 다음과 같습니다.

그런 다음이를 기반으로 계산을 수행하면 결과 값도 시간이 지남에 따라 변경되는 값이됩니다. 예를 들면 다음과 같습니다.

minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;

이 예제에서는 minX항상 마우스 포인터의 x 좌표보다 16이 작습니다. 반응 인식 라이브러리를 사용하면 다음과 같이 말할 수 있습니다.

rectangle(minX, minY, maxX, maxY)

마우스 포인터 주위에 32x32 상자가 그려지고 움직일 때마다 추적합니다.

다음은 기능적 반응성 프로그래밍에 대한 아주 좋은 논문입니다 .


25
그렇다면 리 액티브 프로그래밍은 선언적 프로그래밍의 한 형태입니까?
troelskn 2016 년

31
> 반응 형 프로그래밍은 선언적 프로그래밍의 한 형태입니까? 기능적 반응성 프로그래밍은 기능적 프로그래밍의 한 형태로, 선언적 프로그래밍의 한 형태입니다.
Conal

7
사실은 아닙니다. 예를 들어, sqrt(x)매크로로 C를 호출하면 계산 sqrt(mouse_x())하고 두 배로 돌려줍니다. 진정한 기능적 반응 형 시스템에서는 sqrt(x)새로운 "시간에 따른 두 배"를 반환합니다. FR 시스템을 시뮬레이션하려고한다면 #define매크로를 선호하는 변수를 맹세해야합니다. FR 시스템은 일반적으로 재 계산이 필요할 때만 물건을 다시 계산하지만 매크로를 사용하면 하위 표현에 이르기까지 모든 것을 지속적으로 재평가해야합니다.
Laurence Gonsalves 16:26에

4
"많은 유형의 소프트웨어 (예 : 사용자 상호 작용이있는 모든 것)의 부작용은 어느 정도 수준에서 필요합니다." 아마도 구현 수준에서만 가능합니다. 순수하고 게으른 함수형 프로그래밍의 구현에는 많은 부작용이 있으며 패러다임의 성공 중 하나는 이러한 영향 중 많은 것을 프로그래밍 모델에서 제외시키는 것입니다. 기능적인 사용자 인터페이스에 대한 필자의 의도는 부작용없이 완전히 프로그래밍 할 수 있음을 시사한다.
Conal

4
@tieTYT x는 절대 재 할당 / 돌연변이되지 않습니다. x의 값은 시간에 따른 일련의 값입니다. 그것을 보는 또 다른 방법은 x가 숫자와 같이 "정상적인"값을 갖는 대신에 x의 값은 (개념적으로) 매개 변수로 시간이 걸리는 함수라는 것입니다. (이것은 약간 단순화 된 것입니다. 마우스 위치와 같은 것들의 미래를 예측할 수있는 시간 값을 만들 수 없습니다.)
Laurence Gonsalves

144

프로그램이 스프레드 시트이고 모든 변수가 셀이라는 것을 상상하는 것이 첫 번째 직관에 도달하는 쉬운 방법입니다. 스프레드 시트의 셀 중 하나라도 변경되면 해당 셀을 참조하는 모든 셀도 변경됩니다. FRP와 동일합니다. 이제 일부 셀이 자체적으로 변경되거나 외부 환경에서 가져 온다고 상상해보십시오. GUI 상황에서 마우스의 위치가 좋은 예가됩니다.

그것은 반드시 많은 것을 놓친다. 실제로 FRP 시스템을 사용하면 은유가 매우 빠르게 분해됩니다. 우선, 개별 이벤트 (예 : 마우스 클릭)도 모델링하려는 시도가 있습니다. 나는 이것이 당신에게 그것이 어떤 것인지 생각하기 위해 여기에 두는 것입니다.


3
매우 apposite 예입니다. 이론적 인 내용을 갖는 것이 좋으며 아마도 일부 사람들은 기초적인 예에 ​​의지하지 않고 그 의미를 얻을 수 있지만 추상적 인 것이 아니라 나를 위해 무엇을 해야하는지부터 시작해야합니다. 내가 최근에 얻은 것은 (Netflix의 Rx 대화에서 얻었습니다!) RP (또는 Rx, 어쨌든)는 이러한 "가치 변경"을 일류로 만들고 그에 대해 추론하거나 그와 관련된 기능을 작성하게합니다. 원하는 경우 스프레드 시트 또는 셀을 작성하는 함수를 작성하십시오. 또한 값이 끝나는 시점을 처리하고 자동으로 정리할 수 있습니다.
Benjohn

이 예제는 지능적인 라우팅을 사용하기 위해 종속성을 선언하는 이벤트 중심 프로그래밍과 반응 방식의 차이점을 강조합니다.
Kinjelom

131

나에게 그것은 상징의 두 가지 다른 의미입니다 =.

  1. 수학에서 x = sin(t)수단, 즉 x이다 다른 이름 에 대한 sin(t). 따라서 글쓰기 x + y는와 같습니다 sin(t) + y. 함수형 리 액티브 프로그래밍은 이런 점에서 수학과 같습니다. 작성하면 사용 당시 x + y의 값이 무엇이든 계산 t됩니다.
  2. C와 유사한 프로그래밍 언어 (제국 언어)에서 x = sin(t)할당은 할당시 할당 x 을 저장 한다는 의미입니다 sin(t).

5
좋은 설명입니다. FRP의 의미에서 "시간"은 일반적으로 "외부 입력과의 변경"이라고 덧붙일 수 있다고 생각합니다. 외력이 FRP의 입력을 변경할 때마다 "시간"을 앞당기 고 변경의 영향을받는 모든 것을 다시 계산합니다.
Didier A.

4
수학에서 x = sin(t)평균 xsin(t)주어진 값입니다 t. 기능적 으로 다른 이름 이 아닙니다sin(t) . 그렇지 않으면입니다 x(t) = sin(t).
Dmitri Zaitsev

+ Dmitri Zaitsev Equals 부호는 수학에서 몇 가지 의미를 갖습니다. 그중 하나는 왼쪽이 보일 때마다 오른쪽으로 바꿀 수 있다는 입니다. 예를 들어 2 + 3 = 5또는 a**2 + b**2 = c**2.
user712092

71

좋아, 배경 지식과 당신이 지적한 Wikipedia 페이지를 읽음으로써, 반응성 프로그래밍은 데이터 흐름 컴퓨팅과 유사하지만 특정 외부 "자극"이 발생하여 노드 세트를 실행하여 계산을 수행하는 것으로 보입니다.

예를 들어 음악 재생 응용 프로그램의 볼륨 컨트롤과 같은 사용자 인터페이스 컨트롤을 터치하면 다양한 표시 항목과 실제 ​​오디오 출력 볼륨을 업데이트해야하는 UI 디자인에 매우 적합합니다. 직접 그래프에서 노드와 관련된 값을 수정하는 것과 일치하는 볼륨 (슬라이더라고 가정)을 수정하면

해당 "볼륨 값"노드의 가장자리를 가진 다양한 노드가 자동으로 트리거되며 필요한 계산 및 업데이트가 자연스럽게 응용 프로그램을 통해 리플됩니다. 응용 프로그램은 사용자 자극에 "반응"합니다. 기능적 반응 형 프로그래밍은 기능적 언어 또는 일반적으로 기능적 프로그래밍 패러다임 내에서이 아이디어를 구현 한 것일뿐입니다.

"데이터 흐름 컴퓨팅"에 대한 자세한 내용은 Wikipedia에서 또는 즐겨 찾는 검색 엔진을 사용하여이 두 단어를 검색하십시오. 일반적인 아이디어는 다음과 같습니다. 프로그램은 노드에 대한 유 방향 그래프이며 각각 간단한 계산을 수행합니다. 이러한 노드는 일부 노드의 출력을 다른 노드의 입력에 제공하는 그래프 링크를 통해 서로 연결됩니다.

노드가 실행되거나 계산을 수행하면 해당 출력에 연결된 노드에 해당 입력이 "트리거"또는 "표시"됩니다. 모든 입력이 트리거 / 표시 / 사용 가능한 모든 노드가 자동으로 실행됩니다. 반응 형 프로그래밍이 정확히 어떻게 구현되는지에 따라 그래프가 암시 적이거나 명시적일 수 있습니다.

노드는 병렬로 실행되는 것으로 볼 수 있지만 종종 직렬 또는 병렬 처리가 제한적으로 실행됩니다 (예 : 실행하는 스레드가 몇 개있을 수 있음). 유명한 예로 맨체스터 데이터 흐름 시스템 (IIRC)이 태그 데이터 아키텍처를 사용하여 하나 이상의 실행 단위를 통해 그래프의 노드 실행을 예약했습니다. 데이터 흐름 컴퓨팅은 계산 계산을 비동기 적으로 발생시키는 트리거링 계산이 클록 (또는 클럭)에 의해 실행되도록 시도하는 것보다 더 효과적으로 작동하는 상황에 매우 적합합니다.

리 액티브 프로그래밍은이 "캐스케이드 실행"아이디어를 가져 와서 데이터 흐름과 같은 방식으로 프로그램을 생각하는 것처럼 보이지만 일부 노드는 "외부 세계"에 연결되어 있고 이러한 감각이 실행되면 캐스케이드 실행이 트리거됩니다. 같은 노드가 변경됩니다. 프로그램 실행은 복잡한 반사 아크와 유사한 것으로 보입니다. 프로그램은 기본적으로 자극들 사이에서 재채기 일 수도 있고 아닐 수도 있고, 또는 자극들 사이에서 기본적으로 재채기 상태로 정착 될 수도있다.

"비 반응성"프로그래밍은 실행 흐름과 외부 입력과의 관계에 대해 매우 다른 관점에서 프로그래밍하는 것입니다. 사람들은 외부 입력에 반응하는 어떤 것이라도 그들에게 "반응"한다고 말하고 싶은 유혹을받을 것이기 때문에 다소 주관적 일 수 있습니다. 그러나 문제의 정신을 살펴보면 고정 간격으로 이벤트 큐를 폴링하고 함수 (또는 스레드)에있는 이벤트를 디스패치하는 프로그램은 반응성이 떨어집니다 (고정 간격으로 사용자 입력에만 참석하기 때문에). 다시 한 번 말하지만, 이것은 바로 폴링 간격이 빠른 폴링 구현을 매우 낮은 수준의 시스템에 배치하고 그 위에 반응 방식으로 프로그램하는 것을 상상할 수 있습니다.


1
자, 위의 좋은 답변이 있습니다. 게시물을 삭제해야합니까? 두 사람 또는 세 사람이 아무 것도 추가하지 않는다고 말하면 도움이되는 수가 증가하지 않으면 삭제합니다. 가치있는 것을 추가하지 않는 한 여기에 두지 않아도됩니다.
Thomas Kammeyer

3
데이터 흐름에 대해 언급 했으므로 IMHO의 가치가 더해집니다.
Rainer Joswig 2006 년

이것이 QML의 의미입니다.;)
mlvljr

3
나에게이 대답은 이해하기가 가장 쉽다. 특히 "응용 프로그램을 통한 리플"및 "감각 같은 노드"와 같은 자연적인 아날로그를 사용하기 때문이다. 큰!
Akseli Palén

1
불행히도 Manchester Dataflow Machine 링크가 작동하지 않습니다.
Pac0

65

FRP에 대한 많은 페이지를 읽은 후 마침내 FRP에 대한 깨달은 글을 보았고, 마침내 FRP가 무엇에 관한 것인지 이해할 수있었습니다.

나는 아래에 Heinrich Apfelmus (반응 바나나의 저자)를 인용합니다.

기능적 반응성 프로그래밍의 본질은 무엇입니까?

일반적인 대답은“FRP는 변경 가능한 상태 대신 시변 함수 측면에서 시스템을 설명하는 것입니다.”라는 것이 확실합니다. 이것이 의미 론적 관점이다. 그러나 내 의견으로는 다음과 같은 순전히 구문 기준으로 더 깊고 더 만족스러운 답변을 얻을 수 있습니다.

기능적 반응 형 프로그래밍의 본질은 선언시 값의 동적 동작을 완전히 지정하는 것입니다.

예를 들어 카운터의 예를 들어 보면 카운터를 늘리거나 줄이는 데 사용할 수있는“위로”및“아래로”라는 두 개의 단추가 있습니다. 반드시 초기 값을 지정한 다음 버튼을 누를 때마다 변경해야합니다. 이 같은:

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)

요점은 선언시 카운터의 초기 값 만 지정한다는 것입니다. 카운터의 동적 동작은 나머지 프로그램 텍스트에 내재되어 있습니다. 반대로, 기능적 반응 형 프로그래밍은 선언시 다음과 같이 전체 동적 동작을 지정합니다.

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) eventUp
             `union` fmap (subtract 1) eventDown)

카운터의 역학을 이해하고 싶을 때마다 그 정의 만 봐야합니다. 발생할 수있는 모든 것이 오른쪽에 나타납니다. 이는 후속 선언이 이전에 선언 된 값의 동적 동작을 변경할 수있는 명령 방식과는 대조적입니다.

그래서,에 대한 이해 FRP 프로그램은 방정식의 집합입니다 : 여기에 이미지 설명을 입력하십시오

j 이산 : 1,2,3,4 ...

f에 따라 t이 외부 자극을 모델링 할 수있는 possiblilty을 통합 있도록

프로그램의 모든 상태는 변수로 캡슐화됩니다 x_i

FRP 라이브러리는 진행 시간, 즉을 수행 j합니다 j+1.

비디오 에서 이러한 방정식을 훨씬 자세히 설명 합니다.

편집하다:

원래 답변 후 약 2 년이 지난 최근에는 FRP 구현에 또 다른 중요한 측면이 있다는 결론에 도달했습니다. 캐시 무효화와 같은 중요한 실제 문제를 해결해야합니다 .

x_i-s에 대한 방정식 은 종속성 그래프를 나타냅니다. x_i시간에 변경 사항 중 일부가있을 때 j다른 x_i'j+1을 모두 업데이트 할 필요는 없으므로 일부 x_i'가 독립되어있을 수 있으므로 모든 종속성을 다시 계산할 필요는 없습니다 x_i.

또한 x_i변경하는 -s를 증분 업데이트 할 수 있습니다. 예를 들어,의는지도 작업을 생각해 보자 f=g.map(_+1)스칼라에서 f하고 g있는 List의를 Ints. 여기 f에 대응 x_i(t_j)하고 g있다 x_j(t_j). 이제 요소를 앞에 추가하면의 모든 요소에 대한 작업 g을 수행하는 것이 낭비 map입니다 g. 일부 FRP 구현 (예 : reflex-frp )은이 문제를 해결하는 것을 목표로합니다. 이 문제는 증분 컴퓨팅 이라고도 합니다.

즉, x_iFRP 의 동작 ( -s)은 캐시 계산으로 간주 될 수 있습니다. x_i-s 중 일부 f_i가 변경 되면 이러한 캐시 ( -s) 를 효율적으로 무효화하고 다시 계산하는 것이 FRP 엔진의 임무입니다 .


4
나는 당신이 이산 방정식으로 갈 때까지 바로 거기에있었습니다 . FRP의 창립 아이디어는 지속적인 시간이었습니다 " j+1" 가없는 . 대신, 연속 시간의 기능을 생각하십시오. Newton, Leibniz 및 다른 사람들이 우리에게 보여 주었 듯이, ODE의 정수와 시스템을 사용하여 이러한 기능을 차등 적이지만 지속적으로 그렇게 묘사하는 것이 종종 매우 편리합니다 (그리고 문자 그대로의 "자연"). 그렇지 않으면 사물 자체 대신 근사 알고리즘 (및 불량 알고리즘)을 설명하고 있습니다.
Conal

HTML 템플릿 및 레이아웃 제약 조건 언어 layx 는 FRP의 요소를 표현하는 것 같습니다.

@Conal FRP가 ODE와 어떻게 다른지 궁금합니다. 그것들은 어떻게 다릅니 까?
jhegedus

@jhegedus 통합 (재귀 적, 즉 ODE)은 전체가 아니라 FRP의 구성 요소 중 하나를 제공합니다. FRP 어휘의 모든 요소 (통합을 포함하지만 이에 국한되지 않음)는 연속 시간으로 정확하게 설명됩니다. 그 설명이 도움이됩니까?
Conal


29

면책 조항 : 내 대답은 Javascript 용 '반응성 프로그래밍'라이브러리 rx.js의 맥락에 있습니다.

함수형 프로그래밍에서는 컬렉션의 각 항목을 반복하는 대신 컬렉션 자체에 고차 함수 (HoF)를 적용합니다. 따라서 FRP의 기본 개념은 각 개별 이벤트를 처리하는 대신 이벤트 스트림 (관측 가능 *으로 구현)을 생성하고 대신 HoF를 적용한다는 것입니다. 이렇게하면 게시자와 가입자를 연결하는 데이터 파이프 라인으로 시스템을 시각화 할 수 있습니다.

Observable 사용의 주요 장점은 다음과 같습니다.
i) 코드에서 상태를 추상화합니다. 예를 들어 이벤트 핸들러가 모든 'n'번째 이벤트에 대해서만 시작되거나 첫 번째 'n'이벤트 후에 실행을 중지하려는 경우, 또는 첫 번째 'n'이벤트 후에 만 ​​발사를 시작하면 카운터 설정, 업데이트 및 확인 대신 HoF (필터, takeUntil, 건너 뛰기)를 사용할 수 있습니다.
ii) 코드 지역성을 향상시킵니다. 구성 요소의 상태를 변경하는 5 개의 다른 이벤트 핸들러가있는 경우, Observable을 병합하고 대신 병합 된 Observable에 단일 이벤트 핸들러를 정의하여 5 개의 이벤트 핸들러를 1로 효과적으로 결합 할 수 있습니다. 전체 시스템의 이벤트가 구성 요소에 영향을 줄 수있는 이벤트에 대해 추론하기 쉽습니다. 모든 이벤트가 단일 처리기에 존재하기 때문입니다.

  • Observable은 Iterable의 이중입니다.

Iterable은 느리게 소비되는 시퀀스입니다. 각 항목은 사용하고자 할 때마다 반복자가 가져 오기 때문에 열거자는 소비자에 의해 구동됩니다.

Observable은 느리게 생성 된 시퀀스입니다. 각 항목은 시퀀스에 추가 될 때마다 관찰자에게 푸시되므로 열거자는 생산자에 의해 구동됩니다.


1
Observable에 대한이 간단한 정의 와 Iterable과의 차별화에 대해 대단히 감사합니다 . 복잡한 개념을 잘 알려진 이중 개념과 비교하여 진정한 이해를 얻는 것이 종종 도움이된다고 생각합니다.

2
"따라서 FRP의 기본 개념은 각 개별 이벤트를 처리하는 대신 이벤트 스트림 (관측 가능 *으로 구현)을 생성하고 대신 HoF를 적용하는 것입니다." 나는 잘못 생각할 수 있지만 이것은 실제로 FRP가 아니라 Observer 디자인 패턴에 대한 훌륭한 추상화로, 명령 코드와 함께 사용되도록 HoF를 통해 기능적 작업을 수행 할 수 있습니다. 주제에 대한 토론 -lambda-the-ultimate.org/node/4982
nqe

18

야, 이건 정말 멋진 아이디어야! 1998 년에 왜 이것에 대해 알지 못했습니까? 어쨌든, Fran 튜토리얼 에 대한 나의 해석은 다음과 같습니다 . 제안은 가장 환영합니다, 나는 이것을 기반으로 게임 엔진을 시작할 생각입니다.

import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy

pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')

class Time:
    def __float__(self):
        return epoch_delta()
time = Time()

class Function:
    def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
        self.var = var
        self.func = func
        self.phase = phase
        self.scale = scale
        self.offset = offset
    def copy(self):
        return copy(self)
    def __float__(self):
        return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
    def __int__(self):
        return int(float(self))
    def __add__(self, n):
        result = self.copy()
        result.offset += n
        return result
    def __mul__(self, n):
        result = self.copy()
        result.scale += n
        return result
    def __inv__(self):
        result = self.copy()
        result.scale *= -1.
        return result
    def __abs__(self):
        return Function(self, abs)

def FuncTime(func, phase = 0., scale = 1., offset = 0.):
    global time
    return Function(time, func, phase, scale, offset)

def SinTime(phase = 0., scale = 1., offset = 0.):
    return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()

def CosTime(phase = 0., scale = 1., offset = 0.):
    phase += pi / 2.
    return SinTime(phase, scale, offset)
cos_time = CosTime()

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    @property
    def size(self):
        return [self.radius * 2] * 2
circle = Circle(
        x = cos_time * 200 + 250,
        y = abs(sin_time) * 200 + 50,
        radius = 50)

class CircleView(Sprite):
    def __init__(self, model, color = (255, 0, 0)):
        Sprite.__init__(self)
        self.color = color
        self.model = model
        self.image = Surface([model.radius * 2] * 2).convert_alpha()
        self.rect = self.image.get_rect()
        pygame.draw.ellipse(self.image, self.color, self.rect)
    def update(self):
        self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)

sprites = Group(circle_view)
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
    screen.fill((0, 0, 0))
    sprites.update()
    sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

간단히 말해 : 모든 구성 요소를 숫자처럼 취급 할 수 있다면 전체 시스템을 수학 방정식처럼 취급 할 수 있습니다.


1
조금 늦었지만 어쨌든 ... Frag는 FRP를 사용하는 게임 입니다.
arx

14

Paul Hudak의 저서, Haskell School of Expression 은 Haskell에 대한 훌륭한 소개 일뿐만 아니라 FRP에 상당한 시간을 소비합니다. FRP를 처음 사용하는 사람이라면 FRP의 작동 방식을 이해하는 것이 좋습니다.

Haskell School of Music 이 책 (2011 년판 , 2014 년판 )의 새로운 재 작성도 있습니다.


10

이전 답변에 따르면 수학적으로 우리는 단순히 높은 순서로 생각하는 것 같습니다. 유형 X를 갖는 값 x 를 생각하는 대신 , 우리는 함수 x : TX를 생각합니다 . 여기서 T 는 시간 유형입니다. 자연수, 정수 또는 연속체입니다. 이제 프로그래밍 언어로 y : = x + 1을 쓸 때 실제로 방정식 y ( t ) = x ( t ) + 1을 의미합니다.


9

표시된대로 스프레드 시트처럼 작동합니다. 일반적으로 이벤트 중심 프레임 워크를 기반으로합니다.

모든 "패러다임"과 마찬가지로 최신 성도 논쟁의 여지가 있습니다.

액터의 분산 플로우 네트워크에 대한 나의 경험을 통해 노드 네트워크 전체의 상태 일관성에 대한 일반적인 문제로 쉽게 넘어갈 수 있습니다. 즉, 이상한 루프에서 많은 진동과 트랩으로 끝납니다.

일부 시맨틱은 참조 루프 나 브로드 캐스트를 암시하므로 피하기 어렵고, 액터 네트워크가 예측할 수없는 상태로 수렴 (또는 수렴)하지 않기 때문에 상당히 혼란 스러울 수 있습니다.

마찬가지로, 전체 상태가 솔루션에서 멀어지기 때문에 가장자리가 잘 정의되어 있어도 일부 상태에 도달하지 못할 수 있습니다. 2 + 2가 2가 된 시점과 그들이 그렇게 머물 렀는지 여부에 따라 2 + 2는 4가 될 수도 있고 아닐 수도 있습니다. 스프레드 시트에는 동기식 클럭과 루프 감지 기능이 있습니다. 분산 액터는 일반적으로 그렇지 않습니다.

모든 좋은 재미 :).



7

이 기사Andre Staltz 는 지금까지 내가 본 최고의 명확 설명입니다.

기사의 일부 인용문 :

반응 형 프로그래밍은 비동기식 데이터 스트림을 사용한 프로그래밍입니다.

또한, 이러한 스트림을 결합, 생성 및 필터링 할 수있는 놀라운 도구 상자가 제공됩니다.

다음은 기사의 일부인 환상적인 다이어그램의 예입니다.

클릭 이벤트 스트림 다이어그램


5

시간이 지남에 따라 (또는 시간을 무시하면서) 수학적 데이터 변환에 관한 것입니다.

코드에서 이것은 기능적 순도와 선언적 프로그래밍을 의미합니다.

상태 버그는 표준 명령형 패러다임에서 큰 문제입니다. 코드의 다양한 비트는 프로그램 실행에서 다른 "시간"에 일부 공유 상태를 변경할 수 있습니다. 다루기가 어렵다.

FRP에서는 선언적 프로그래밍과 같이 데이터가 한 상태에서 다른 상태로 변환되는 방식과 트리거하는 원인을 설명합니다. 따라서 함수는 단순히 입력에 반응하고 현재 값을 사용하여 새로운 입력을 생성하므로 시간을 무시할 수 있습니다. 이는 상태가 변환 노드의 그래프 (또는 트리)에 포함되며 기능적으로 순수함을 의미합니다.

이것은 복잡성과 디버깅 시간을 크게 줄입니다.

수학에서 A = B + C와 프로그램에서 A = B + C의 차이점을 생각해보십시오. 수학에서 당신은 결코 변하지 않을 관계를 묘사하고 있습니다. 프로그램에서 "지금 바로"A는 B + C라고 말합니다. 그러나 다음 명령은 B ++ 일 수 있으며이 경우 A는 B + C와 동일하지 않습니다. 수학 또는 선언적 프로그래밍에서 A는 요청한 시점에 관계없이 항상 B + C와 같습니다.

따라서 공유 상태의 복잡성을 제거하고 시간이 지남에 따라 값을 변경합니다. 프로그램은 추론하기가 훨씬 쉽습니다.

EventStream은 EventStream + 일부 변형 함수입니다.

동작은 EventStream + 메모리의 일부 값입니다.

이벤트가 발생하면 변환 기능을 실행하여 값이 업데이트됩니다. 이것이 생성하는 값은 동작 메모리에 저장됩니다.

N 개의 다른 행동에 대한 변형 인 새로운 행동을 생성하도록 행동을 구성 할 수 있습니다. 이 구성된 값은 입력 이벤트 (행동)가 발생하면 다시 계산됩니다.

"관찰자는 무국적자이므로 드래그 예제 에서처럼 상태 기계를 시뮬레이션하기 위해 종종 이들 중 몇 개가 필요하다. 우리는 위의 변수 경로와 같이 관련된 모든 관찰자가 접근 할 수있는 상태를 저장해야한다."

견적서-관찰자 패턴의 비추천 http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf


이것이 바로 선언적 프로그래밍에 대한 느낌입니다. 나보다 아이디어를 더 잘 설명합니다.
neevek

2

Reactive Programming에 대한 짧고 명확한 설명은 Cyclejs-Reactive Programming 에 표시되며 단순하고 시각적 인 샘플을 사용합니다.

[모듈 / 컴포넌트 / 오브젝트] 가 반응 함 이며 외부 이벤트에 반응하여 자체 상태를 관리 할 책임이 있습니다.

이 방법의 장점은 무엇입니까? 그것은이다 제어의 반전 주로하기 때문에 [모듈 / 컴포넌트 / 오브젝트] 대중들에 대한 개인 방법을 사용하여 캡슐화를 개선 자체에 대한 책임이 있습니다.

완전한 지식의 원천이 아닌 좋은 출발점입니다. 거기에서 더 복잡하고 깊은 종이로 넘어갈 수 있습니다.


0

Rx, Reactive Extensions for .NET을 확인하십시오. 그들은 IEnumerable을 사용하면 기본적으로 스트림에서 '풀링'한다고 지적합니다. IQueryable / IEnumerable에 대한 Linq 쿼리는 집합에서 결과를 '흡입'하는 집합 작업입니다. 그러나 IObservable에 대해 동일한 연산자를 사용하면 '반응하는'Linq 쿼리를 작성할 수 있습니다.

예를 들어 다음과 같은 Linq 쿼리를 작성할 수 있습니다 (mX <100 및 mY <100은 새 Point (mX, mY)를 선택하는 MyObservableSetOfMouseMovements의 m).

Rx 확장 기능을 사용하면 그게 끝입니다. 들어오는 마우스 움직임 스트림에 반응하고 100,100 상자에있을 때마다 그리는 UI 코드가 있습니다 ...


0

FRP는 기능적 프로그래밍 (모든 것의 아이디어에 기반한 프로그래밍 패러다임은 함수 임)과 반응 형 프로그래밍 패러다임 (모든 것이 스트림 (관측자 및 관찰 가능한 철학)이라는 아이디어에 기반한)의 조합입니다. 그것은 세계 최고의 것입니다.

반응 형 프로그래밍에 대한 Andre Staltz 게시물을 확인하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.