└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # 모나드 첫걸음 2 | 3 | ## 람다 함수 4 | 5 | ```javascript 6 | (function (x, y) { 7 | return x + y; 8 | })(1, 2); 9 | ``` 10 | 11 | - es6 arrow function 12 | 13 | ```javascript 14 | ((x, y) => x + y)(1, 2) 15 | 16 | (x => x + 1)(1) 17 | ``` 18 | 19 | ## 모나드 20 | 21 | `컨텍스트`가 있는 `연속된 계산`을 `읽기 쉽게` 만들기 위한 `순수 함수형` 패턴 22 | 23 | - 컨텍스트 24 | - 연속된 계산 25 | - 읽기 쉽게 26 | - 순수 함수형 27 | 28 | - null 체크라는 컨텍스트가 있는 연속된 계산의 단순한 예 29 | 30 | ```javascript 31 | var value1 = getValue("key1"); 32 | if (value1 !== null) { 33 | var value2 = getValue("key2"); 34 | if (value2 !== null) { 35 | ... 36 | } 37 | } 38 | else { 39 | return null; 40 | } 41 | ``` 42 | 43 | - 계산 속에 컨텍스트가 섞여 있다면 계산의 본질을 파악하기(읽기) 어렵다. 44 | - 컨텍스트와 계산을 분리하면 컨텍스트를 재사용할 수 있다. 45 | - 순수 함수형으로 절차적으로 보이는 연속된 계산을 쉽게 표현하기 어렵다. 46 | - 쉬운 순서대로 알아보자 47 | - 읽기 쉽게 48 | - 순수 함수형 49 | - 연속된 계산 50 | - 컨텍스트 51 | 52 | ## 컨텍스트가 있는 연속된 계산을 `읽기 쉽게` 만들기 위한 순수한 함수형 패턴 53 | 54 | - 코드는 추상화(abstraction)를 통해 읽기 쉽게 만들 수 있다. 55 | - 여러운 것과 모르는 것(C 계열 문법, 언어가 아닌 것), 학습 비용 56 | - 모든 코드를 하나의 함수에 쓰지 않는 이유는 복잡하기 때문 57 | 58 | ```clojure 59 | (str "Hello" "World" "!") 60 | ;; str???? 61 | ``` 62 | 63 | - 축약의 단순함과 풀어쓰는 것과 장황함 64 | - multiplication vs `*` 65 | - `1 multiplication 2` vs `1 * 2` 66 | - uniqueness vs `∃!` 67 | - `∃! n ∈ ℕ: n + 5 = 2n.` vs `uniqueness n exists ....` 68 | 69 | ```clojure 70 | (defn str 71 | "With no args, returns the empty string. With one arg x, returns 72 | x.toString(). (str nil) returns the empty string. With more than 73 | one arg, returns the concatenation of the str values of the args." 74 | {:tag String 75 | :added "1.0" 76 | :static true} 77 | (^String [] "") 78 | (^String [^Object x] 79 | (if (nil? x) "" (. x (toString)))) 80 | (^String [x & ys] 81 | ((fn [^StringBuilder sb more] 82 | (if more 83 | (recur (. sb (append (str (first more)))) (next more)) 84 | (str sb))) 85 | (new StringBuilder (str x)) ys))) 86 | ``` 87 | 88 | ## 컨텍스트가 있는 연속된 계산을 읽기 쉽게 만들기 위한 `순수한 함수형` 패턴 89 | 90 | - 순수 91 | - 부수 효과가 없다. 92 | ```javascript 93 | function inc(x) { 94 | console.log("x:", x); // 부수 효과! 95 | return x + 1; 96 | } 97 | ``` 98 | 99 | - 상태가 없다. 100 | ```javascript 101 | var state = 1; 102 | state = 2; // 상태를 변경! 103 | ``` 104 | 105 | - 글로벌 값도 없다. 106 | ```javascript 107 | var y = 1; 108 | 109 | function calc(x) { 110 | return x + y; // 글로벌 y를 참조! 111 | } 112 | ``` 113 | 114 | - 절차도 없다. 115 | ```javascript 116 | // 아레 절차 대로 실행! 117 | var x = 1; 118 | var y = 2; 119 | x + y; 120 | ``` 121 | 122 | - 대부분의 언어는 순수하지 않은 기능을 제공한다. 123 | - 순수한 프로그래밍의 장점은 많지만 여기서 이야기하지 않는다. 124 | - 모나드는 순수함을 전제로 한다. 따라서 순수한 것과 섞어 사용한다면 의미가 없다. 125 | 126 | ## 컨텍스트가 있는 `연속된 계산`을 읽기 쉽게 만들기 위한 순수한 함수형 패턴 127 | 128 | ### 절차적 129 | 130 | ```javascript 131 | var x = 1; 132 | var y = 2; 133 | var result = x + y; 134 | ``` 135 | 136 | ### 순수 함수형 절차적 표현 137 | 138 | ```javascript 139 | ((x => 140 | (y => 141 | x + y))(1))(2) 142 | ``` 143 | 144 | - 절차적인 스타일에 비해 읽기 어렵다. 145 | 146 | ## 들여쓰기를 이용해서 보기 좋게 만들기 147 | 148 | ```javascript 149 | ((x => 150 | (y => 151 | x + y)) 152 | (1)) 153 | (2) 154 | ``` 155 | 156 | ### helper 함수를 이용해서 더 보기 좋게 만들기 157 | 158 | - 인자 하나와 함수를 받아 함수에 인자를 적용하는 함수 159 | 160 | ```javascript 161 | function bind (mv, f) { 162 | return f(mv); 163 | }; 164 | ``` 165 | 166 | - `bind` 함수를 사용해 보기 167 | 168 | ```javascript 169 | bind(1, x => 170 | bind(2, y => 171 | x + y)); 172 | ``` 173 | 174 | - 들여쓰기를 보기 좋게 해보자 175 | 176 | ```javascript 177 | bind(1, x => 178 | bind(2, y => 179 | x + y)); 180 | ``` 181 | 182 | - 절차적 스타일과 비교해보기 183 | 184 | ```javascript 185 | var x = 1; 186 | var y = 2; 187 | var result = x + y; 188 | ``` 189 | 190 | - `bind` 함수도 있고 괄호도 있고 해서 복잡해 보인다? 191 | - `var`도 있고 `=`도 있고 `;`은 왜 복잡해 보이지 않는가? 192 | - `var`, `=`, `;`는 이미 마음 속에 추상화 하지만 `bind` 함수는 내 마음속에 아직 익숙하지 않기 때문이다. 193 | - 이제 `bind` 함수나 괄호 같은 것은 봐도 못본 척하자. 필요한 것은 x에 1을 y에 2를 바인딩하고 x와 y를 더하는 것 194 | 195 | ``` 196 | 1 x 197 | 2 y 198 | x + y 199 | ``` 200 | 201 | - 억지스럽다고 생각하겠지만 순수 함수로 연속된 계산을 읽기 쉽게 표현했다. 202 | 203 | ### 모나드 패턴 204 | 205 | - 연속된 계산을 표현하는 추상 패턴 206 | 207 | ```javascript 208 | bind(계산, 계산 결과를 바인딩할 기호 => // 줄 209 | bind(계산, 계산 결과를 바인딩할 기호 => // 줄 210 | 결과)); // 줄 211 | ``` 212 | 213 | - `bind` 함수를 다시 보자 214 | 215 | ```javascript 216 | function bind (mv, f) { 217 | return f(mv); 218 | }; 219 | ``` 220 | 221 | - 모나드 패턴은 유지하고 `bind` 함수의 구현 방식에 따라 모나드 특성이 결정된다. 222 | - 위 예제는 단순히 절차적 컨텍스트만 가지는 모나드이고 `identity` 모나드라고 부른다. 223 | 224 | ## `컨텍스트`가 있는 연속된 계산을 읽기 쉽게 만들기 위한 순수한 함수형 패턴 225 | 226 | - 계산 과정을 감싸고 있는 공통된 이야기 227 | - 컨텍스트의 예 228 | - 계산 과정에서 결과가 `nil`인 경우 (maybe 모나드) 229 | - 계산 과정에 로그를 기록해야하는 경우 (writer 모나드) 230 | - 계산 과정 중에 `config`가 필요한 경우 참조해야 하는 경우 (reader 모나드) 231 | - 계산 과정 중 상태가 필요한 경우 (state 모나드) 232 | 233 | ## 계산 과정에서 결과가 `nil`인 경우 다음 계산을 하지 않는 `컨텍스트` 예제 234 | 235 | - 계산 과정 `null`이 나올 수 있는 예제 236 | 237 | ```javascript 238 | function getName (id) { 239 | if (id == 1) { 240 | return "WORLD"; 241 | } 242 | else { 243 | return null; 244 | } 245 | } 246 | 247 | bind( getName(2) , x => 248 | bind( x.toLowerCase() , y => 249 | "Hello, " + y + "!" 250 | )); 251 | ``` 252 | 253 | ``` 254 | Uncaught TypeError: Cannot read property 'toLowerCase' of null 255 | at monad.html:77 256 | at bind (monad.html:64) 257 | at monad.html:76 258 | at monad.html:81 259 | ``` 260 | 261 | - `bind` 함수 수정 262 | 263 | ```javascript 264 | function bind (mv, f) { 265 | if (mv != null) { 266 | return f(mv); 267 | } 268 | return null; 269 | }; 270 | ``` 271 | 272 | ```javascript 273 | bind( getName(2) , x => 274 | bind( x.toLowerCase() , y => 275 | "Hello, " + y + "!" 276 | )); 277 | // => null 278 | ``` 279 | 280 | - 계산 중 `null`이 나오면 다음 계산을 진행하지 않고 최종 결과를 `null`로 처리하는 컨텍스트를 가지는 모나드를 `maybe` 모나드라고 한다. 281 | 282 | - 하스켈에서 `maybe` 모나드 283 | 284 | ```haskell 285 | Just 3 >>= (\x -> 286 | Just 4 >>= (\y -> 287 | Just (x + y))) 288 | ``` 289 | 290 | ## 계산 과정에 로그와 같이 뭔가 계속 쓸 수 있어야 하는 경우 (writer 모나드) 291 | 292 | ```javascript 293 | var result = 294 | bind( 1, x => 295 | bind( logging("x: " + x), _ => 296 | bind( 2, y => 297 | x + y 298 | ))); 299 | 300 | // result는 로그와 결과가 함께 들어 있어야 한다. 301 | 302 | getLog(result); 303 | // => ["x: 1"] 304 | // 로그는 계속 쌓여야 하니 배열에 넣자! 305 | 306 | getValue(result); 307 | // => 3 308 | ``` 309 | 310 | - 로그와 계산 결과를 담을 데이터 (타입?) 311 | 312 | ```javascript 313 | // 객체에 담을 수도 있지만 예제를 간단하게 하기 위해서 314 | var result = [3, ["x: 1"]]; 315 | 316 | // v는 [x, y] 형태로 여야한다는 암묵적 타입 317 | function getLog(v) { 318 | return v[1]; 319 | } 320 | 321 | function getValue(v) { 322 | return v[0]; 323 | } 324 | ``` 325 | 326 | - `bind` 함수는 어떻게 생겨야 하나? 327 | 328 | ```javascript 329 | // oldResult는 [x, []] 여야 한다는 암묵적 타입 330 | function bind(oldResult, f) { 331 | var oldValue = oldResult[0]; 332 | var oldLogs = oldResult[1]; 333 | var newResult = f(oldValue); 334 | var newValue = newResult[0]; 335 | var newLogs = newResult[1]; 336 | return [newValue, oldLogs.concat(newLogs)]; 337 | } 338 | ``` 339 | 340 | ```javascript 341 | var result = 342 | bind( 1, x => 343 | bind( logging("x: " + x), _ => 344 | bind( 2, y => 345 | x + y 346 | ))); 347 | ``` 348 | 349 | - 체크는 안해 주지만 타입 에러가 발생 350 | 351 | ```javascript 352 | function bind(oldResult, f) { ... } 353 | 354 | bind(1, function(x) { .. 355 | 356 | // 1 != oldResult 기대하는 타입이 아니다! 357 | ``` 358 | 359 | - 일반 값을 `bind` 함수가 받을 수 있는 타입으로 만들어 주자! 360 | 361 | ```javascript 362 | function bind(oldResult, f) { ... } 363 | 364 | bind([1, []], function(x) { .. 365 | ``` 366 | 367 | - 일반 값을 `bind` 함수가 받을 수 있는 타입으로 만들어주는 helper 함수를 만들자! 368 | 369 | ```javascript 370 | function mreturn(v) { 371 | return [v, []]; 372 | } 373 | ``` 374 | 375 | - 일반적으로 사용하기 위해서 함수 이름을 `newLogType` 대신 `mreturn`을 사용했다. 376 | 모나드에서 보통 `return`이라고 부르지만 자바스크립트에서 `return`이 예약어라서 `mreturn`으로 했다. 377 | 378 | ``` 379 | function bind(oldResult, f) { ... } 380 | 381 | bind(mreturn(1), function(x) { .. 382 | ``` 383 | 384 | ```javascript 385 | var result = 386 | bind( mreturn(1), x => 387 | bind( logging("x: " + x), _ => 388 | bind( mreturn(2), y => 389 | mreturn(x + y) // 최종 결과도 타입을 맞춰 준다. 390 | ))); 391 | ``` 392 | 393 | - `logging` 함수는 어떻게 만들어야 하나? 394 | 395 | ```javascript 396 | function logging(log) { 397 | return [null, [log]]; 398 | } 399 | ``` 400 | 401 | - 값이 있어야하는 부분이 `null`인 이유는 다음 계산 과정에서 사용하지 않기(`_`) 때문이다. 402 | - 완성 버전 403 | 404 | ```javascript 405 | function bind(mv, f) { 406 | var vv = f(mv[0]); 407 | return [vv[0], mv[1].concat(vv[1])]; 408 | } 409 | 410 | function mreturn(v) { 411 | return [v, []]; 412 | } 413 | 414 | function logging(log) { 415 | return [null, [log]]; 416 | } 417 | 418 | function getLog(v) { 419 | return v[1]; 420 | } 421 | 422 | function getValue(v) { 423 | return v[0]; 424 | } 425 | 426 | var result = 427 | bind( mreturn(1), x => 428 | bind( logging("x: " + x), _ => 429 | bind( mreturn(2), y => 430 | mreturn(x + y) 431 | ))); 432 | 433 | getLog(result); 434 | // ["x: 1"] 435 | getValue(result); 436 | // 3 437 | ``` 438 | 439 | - 계산 과정중 계산 결과에 영향을 주지 않고 다른 곳에 값을 기록할 수 있는 컨텍스트를 가지는 모나드를 writer 440 | 모나드라고 한다. 441 | - `mreturn` helper 함수는 일반 값을 모나드 컨텍스트 안에서 사용할 수 있는 형태(타입)으로 바꿔주는 함수다. 442 | - 모나드는 `bind` 함수와 `return` 함수에 따라 특성이 결정된다. 443 | - writer 모나드에서 `logging` 함수는 `tell` 함수라고 부른다. 444 | 445 | ### 모노이드 446 | - 합치는 것에 대한 추상화(인터페이스) 447 | - `concat` 대신 추상적인 함치는 방법에 대한 `append` 함수 정의 448 | ```javascript 449 | function bind(mv, f) { 450 | var vv = f(mv[0]); 451 | return [vv[0], mv[1].concat(vv[1])]; // -> mv가 모노이드면 append를 사용 452 | } 453 | ``` 454 | - 빈 배열 대신 합쳐도 자신이 나오는 `empty` 타입 정의 455 | ```javascript 456 | function mreturn(v) { 457 | return [v, []]; // -> [] 대신 mv 타입에 대한 empty 함수를 사용, 타입 추론이 약한 언어에서는 재사용하려면 조금 문법이 복잡하다. 458 | } 459 | ``` 460 | 461 | ## 계산 과정 중에 `config`와 같이 언제든 참조할 수 있는 환경 값이 필요한 경우 (reader 모나드) 462 | 463 | ```javascript 464 | var config = { debug: true }; 465 | 466 | function calc1 (x) { 467 | return 1 + x; 468 | } 469 | 470 | function calc2 (y) { 471 | if (config.debug == true) { // 컨텍스트 외부 값을 값을 참조 472 | return 2 + y; 473 | } 474 | else { 475 | return 0; 476 | } 477 | } 478 | 479 | var x = calc1(1); 480 | var y = calc2(x); 481 | var result = x + y; 482 | ``` 483 | 484 | - `calc2`는 전역 변수인 `config`를 참조한다. 485 | 486 | - 전역 변수를 사용하지 않는 `calc2` 487 | 488 | ```javascript 489 | function calc1 (x) { 490 | return 1 + x; 491 | } 492 | 493 | function calc2 (y, config) { 494 | if (config.debug == true) { 495 | return 2 + y; 496 | } 497 | else { 498 | return 0; 499 | } 500 | } 501 | 502 | var config = { debug: true }; 503 | var x = calc1(1); 504 | var y = calc2(x, config); 505 | var result = x + y; 506 | ``` 507 | 508 | - 모나드로 풀어보자. 509 | 510 | ```javascript 511 | var result = 512 | bind( mreturn(calc1(1)) , x => 513 | bind( ask() , config => 514 | bind( mreturn(calc2(2, config)) , y => 515 | mreturn(x + y) 516 | ))); 517 | 518 | result({debug: true}); 519 | ``` 520 | 521 | - `bind` 함수와 `mresult` 함수 522 | 523 | ```javascript 524 | function bind(mv, f) { 525 | return r => f(mv(r))(r); 526 | } 527 | 528 | function mreturn(v) { 529 | return r => v; 530 | } 531 | ``` 532 | 533 | - 구조가 어렵지만 writer 타입과 다르게 환경 값을 저장하기 위해 함수 값을 사용한다는 것을 알 수 있다. 534 | 535 | - 환경 값을 읽어오기 위한 도우미 함수 536 | 537 | ```javascript 538 | function ask() { 539 | return r => r; 540 | } 541 | ``` 542 | 543 | ```javascript 544 | function calc1 (x, config) { 545 | return 1 + x; 546 | } 547 | 548 | function calc2 (y, config) { 549 | if (config.debug == true) { 550 | return 2 + y; 551 | } 552 | else { 553 | return 0; 554 | } 555 | } 556 | 557 | var result = 558 | bind( mreturn(calc1(1)) , x => 559 | bind( ask() , config => 560 | bind( mreturn(calc2(2, config)) , y => 561 | mreturn(x + y) 562 | ))); 563 | 564 | result({debug: true}); 565 | // 6 566 | result({debug: false}); 567 | // 2 568 | ``` 569 | 570 | - 처음에 환경 값을 넘기고 계산 과정 중 언제든 참조할 수 있도록 하는 컨텍스트를 가지는 모나드를 `reader` 571 | 모나드라고 한다. 572 | 573 | ## 계산 과정 중 읽고 쓸 수 있는 상태가 필요한 경우 (state 모나드) 574 | 575 | - 순수 함수에는 상태가 없지만 모나드로 상태를 표현할 수 있다. 576 | 577 | ```javascript 578 | var state = undefined; 579 | var x = 1; 580 | state = 1; // put state 581 | y = state; // get state 582 | var result = x + y; 583 | ``` 584 | 585 | - 모나드로 상태를 표현해 보기 586 | 587 | ```javascript 588 | var result = 589 | bind( mreturn(1) , x => 590 | bind( put(x) , _ => 591 | bind( get() , y => 592 | mreturn(x + y) 593 | ))); 594 | 595 | getValue(result); 596 | ``` 597 | 598 | - `bind`와 `mreturn` 함수 599 | 600 | ```javascript 601 | function bind(mv, f) { 602 | return s => { 603 | [v, ss] = mv(s); 604 | return (f(v))(ss); 605 | } 606 | }; 607 | 608 | function mreturn(v) { 609 | return s => [v, s]; 610 | }; 611 | ``` 612 | 613 | - `reader` 모나드처럼 모나드 값이 함수로 되어 있다. 614 | - 상태 값을 쓰거나 읽을 수 있는 helper 함수 615 | 616 | ```javascript 617 | function put(s) { 618 | return _ => [null, s]; 619 | }; 620 | 621 | function get() { 622 | return s => [s, s]; 623 | }; 624 | 625 | function getValue(s) { 626 | return s()[0]; 627 | }; 628 | 629 | function getState(s) { 630 | return s()[1]; 631 | }; 632 | 633 | var result = 634 | bind( mreturn(1) , x => 635 | bind( put(x) , _ => 636 | bind( get() , y => 637 | mreturn(x + y) 638 | ))); 639 | 640 | getValue(result); 641 | // 2 642 | ``` 643 | 644 | - 계산 과정 중 언제든 하나의 상태를 쓰거나 읽을 수 있는 컨텍스트를 가지는 것을 `state` 모나드라고 부른다. 645 | 646 | ## 정리 647 | 648 | `컨텍스트`가 있는 `연속된 계산`을 `읽기 쉽게` 만들기 위한 `순수한 함수형` 패턴 649 | 650 | ```javascript 651 | bind(mreturn(1), x => bind( mreturn(2), y => mreturn(x + y))); 652 | ``` 653 | 654 | ``` 655 | 모나드 값 -> bind -> 값 -> f1 -> 값 -> return -> 모나드 값 -> bind -> 값 -> f2 -> 값 -> return -> 모나드 값 -> .... 656 | 657 | 값 -> f -> 값 ==변환==> 값 -> f2 -> 값 658 | ``` 659 | 660 | ## 모나드와 타입과 다형성 661 | 662 | ### 데이터와 함수를 묶기 663 | 664 | - writer 모나드에서 `bind` 함수와 `result` 함수 665 | 666 | ```javascript 667 | function bind(v, f) { 668 | var vv = f(v[0]); 669 | return [vv[0], v[1].concat(vv[1])]; 670 | } 671 | 672 | function mreturn(v) { 673 | return [v, []]; 674 | } 675 | ``` 676 | 677 | - `bind`에 전달되는 `v` 데이터는 `[x, []]` 타입이다. 678 | - `mreturn`에서 나오는 데이터는 `[x, []]` 타입이다. 679 | 680 | - `[x, []]` 타입 대신 Writer 타입을 만든다면 ... 681 | 682 | ```javascript 683 | function Writer(value, extra) { 684 | this.value = value; 685 | this.extra = extra; 686 | this.bind = function(f) { 687 | var w = f(this.value); 688 | return new Writer(w.value, this.extra.concat(w.extra)); 689 | }; 690 | } 691 | 692 | Writer.return = function(v) { 693 | return new Writer(v, []); 694 | }; 695 | 696 | Writer.logging = function(log) { 697 | return new Writer(null, [log]); 698 | }; 699 | 700 | var result = 701 | Writer.return(1) .bind(x => 702 | Writer.logging("x: " + x).bind(_ => 703 | Writer.return(2) .bind(y => 704 | Writer.return(x + y); 705 | ))); 706 | 707 | // result.extra; 708 | ``` 709 | 710 | ### 데이터와 함수를 분리 711 | 712 | ```javascript 713 | var writer = { 714 | bind : (mv, f) => { 715 | var vv = f(mv[0]); 716 | return [vv[0], mv[1].concat(vv[1])]; 717 | }, 718 | return : v => [v, []]; 719 | }; 720 | 721 | var domonad = (monad, f) => f(monad.bind, monad.return); 722 | 723 | domonad(writer, (bind, mreturn) => 724 | bind( mreturn(1), x => 725 | bind( logging("x: " + x), _ => 726 | bind( mreturn(2), y => 727 | mreturn(x + y) 728 | )))); 729 | ``` 730 | 731 | - `bind`, `mreturn` 인터페이스 732 | - `do` 노테이션 733 | 734 | ```haskell 735 | thing1 >>= (\x -> func1 x >>= (\y -> thing2 736 | >>= (\_ -> func2 y >>= (\z -> return z)))) 737 | 738 | do 739 | x <- thing1 740 | y <- func1 x 741 | thing2 742 | z <- func2 y 743 | return z 744 | ``` 745 | 746 | - 데이터와 함수를 묶거나 데이터와 함수를 분리하거나 다형성은 가능하다. expression problem(https://en.wikipedia.org/wiki/Expression_problem) 747 | - 하스켈 타입 추론에 의한 다형성 748 | ```haskell 749 | type GameValue = Int 750 | type GameState = (Bool, Int) 751 | 752 | playGame :: String -> State GameState GameValue 753 | playGame [] = do 754 | (_, score) <- get 755 | return score 756 | 757 | playGame (x:xs) = do 758 | (on, score) <- get 759 | case x of 760 | 'a' | on -> put (on, score + 1) 761 | 'b' | on -> put (on, score - 1) 762 | 'c' -> put (not on, score) 763 | _ -> put (on, score) 764 | playGame xs 765 | 766 | startState = (False, 0) 767 | 768 | main = print $ evalState (playGame "abcaaacbbcabbab") startState 769 | ``` 770 | 771 | ## 다루지 않은 내용 772 | 773 | - 펑터와 어플리커티브 펑터 774 | 775 | ```haskell 776 | instance Functor m where 777 | fmap = liftM 778 | 779 | instance Applicative m where 780 | pure = {- move the definition of `return` from the `Monad` instance here -} 781 | (<*>) = ap 782 | ``` 783 | 784 | - 모나드의 추가 인터페이스 785 | 786 | ```hasekll 787 | class Monad m where 788 | (>>=) :: m a -> ( a -> m b) -> m b 789 | (>>) :: m a -> m b -> m b 790 | return :: a -> m a 791 | fail :: String -> m a 792 | ``` 793 | 794 | - 모나드의 법칙 795 | 796 | ```haskell 797 | return a >>= k = k a 798 | m >>= return = m 799 | m >>= (\x -> k x >>= h) = (m >>= k) >>= h 800 | ``` 801 | 802 | - 모나드 합성 803 | - Monad Transformer (https://page.mi.fu-berlin.de/scravy/realworldhaskell/materialien/monad-transformers-step-by-step.pdf) 804 | - Effect (http://okmij.org/ftp/Haskell/extensible/tutorial.html) 805 | 806 | ## 가치 807 | 808 | - 순수 함수안에서 가치를 가진다 809 | - 언어에서 순수하지 않은 것을 허용하지 않는다면 코드의 표현력을 높혀준다. 810 | - 계산 과정에 중복 함수를 제거 811 | - 계산 과정에서 컨텍스트를 로직과 분리해서 생각 812 | - 함수형 프로그래밍 센스 813 | - 비 순수 함수형 언어라면 언어의 철학에 맞게 언어에서 제공하는 비순수 기능과 함수형 기능을 적절히 활용하는 것이 좋다. 814 | 815 | ```javascript 816 | bind(1, x => 817 | bind(2, y => 818 | x + y)); 819 | 820 | var x = 1; 821 | var y = 2; 822 | var result = x + y; 823 | ``` 824 | 825 | ## 사례 826 | 827 | https://wiki.haskell.org/Monad 828 | 829 | - Identity monad - the trivial monad. 830 | - Optional results from computations - error checking without null. 831 | - Random values - run code in an environment with access to a stream of random numbers. 832 | - Read only variables - guarantee read-only access to values. 833 | - Writable state - i.e. log to a state buffer 834 | - A supply of unique values - useful for e.g. guids or unique variable names 835 | - ST - memory-only, locally-encapsulated mutable variables. Safely embed mutable state inside pure functions. 836 | - Global state - a scoped, mutable state. 837 | - Undoable state effects - roll back state changes 838 | - Function application - chains of function application. 839 | - Functions which may error - track location and causes of errors. 840 | - Atomic memory transactions - software transactional memory 841 | - Continuations - computations which can be interrupted and resumed. 842 | - IO - unrestricted side effects on the world 843 | - Search monad - bfs and dfs search environments. 844 | - non-determinism - interleave computations with suspension. 845 | - stepwise computation - encode non-deterministic choices as stepwise deterministic ones 846 | - Backtracking computations 847 | - Region allocation effects 848 | - LogicT - backtracking monad transformer with fair operations and pruning 849 | - concurrent events and threads - refactor event and callback heavy programs into straight-line code via co-routines 850 | - QIO - The Quantum computing monad 851 | - Pi calculus - a monad for Pi-calculus style concurrent programming 852 | - Commutable monads for parallel programming 853 | - Simple, Fair and Terminating Backtracking Monad 854 | - Typed exceptions with call traces as a monad 855 | - Breadth first list monad 856 | - Continuation-based queues as monads 857 | - Typed network protocol monad 858 | - Non-Determinism Monad for Level-Wise Search 859 | - Transactional state monad 860 | - A constraint programming monad 861 | - A probability distribution monad 862 | - Sets - Set computations 863 | - HTTP - http connections as a monadic environment 864 | - Memoization - add memoization to code 865 | --------------------------------------------------------------------------------