-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
924 lines (924 loc) · 126 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="description" content="most opinionated blog"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="alternative" href="/atom.xml" title="devthewild" type="application/atom+xml"><link rel="icon" href="/favicon.png"><title>devthewild</title>
<link rel="stylesheet" href="/css/main.css">
<!--[if lt IE 9]><script>(function(a,b){a="abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video".split(" ");for(b=a.length-1;b>=0;b--)document.createElement(a[b])})()</script><![endif]--><meta name="generator" content="Hexo 5.4.0"></head><body><header class="head"><h1 class="head-title u-fl"><a href="/">devthewild</a></h1><nav class="head-nav u-fr"><ul class="head-nav__list"><li class="head-nav__item"><a href="/" class="head-nav__link">Home</a></li><li class="head-nav__item"><a href="/archives" class="head-nav__link">Archives</a></li></ul></nav></header><main class="main"><article class="post"><header class="post__head"><time datetime="2021-06-01T13:44:34.000Z" class="post__time">June 1, 2021</time><h1 class="post__title"><a href="/2021/06/01/after-coursera-epis/">Coursera - EPiS 후기</a></h1></header><div class="post__main echo"><p>Scala 3(a.k.a Dotty)의 업데이트와 함께 새로운 스칼라 입문 코스, <a target="_blank" rel="noopener" href="https://www.coursera.org/learn/effective-scala">Effective Programming in Scala</a>가 코세라에 올라왔다. <a target="_blank" rel="noopener" href="https://www.youtube.com/watch?v=MSDJ7ehjrqo">소개 영상</a>에 의하면 전제조건은 다른 프로그래밍 언어의 경험이 어느 정도 있을 것, 목표는 스칼라로 업무가 가능한 정도까지이다. 스칼라 입문이지 프로그래밍 입문이 아닌만큼 기본 개념에 대한 설명은 생략하고 다른 언어들에서 쓰이는 개념들은 스칼라에서 어떻게 쓰는지, 함수형으로는 어떻게 같은 논리를 구현하는지에 대해 초점이 맞춰있고 스칼라2에서는 어떻게 썼는지에 대해 차이점도 소개한다. 수업을 들으면서 정리를 좀 남기긴 했지만 스칼라 문법에 대한 이야기를 굳이 요약하기보다 수업을 따라 좋은 설명을 듣기를 추천하고 스칼라 2사용자들에게 유용한 내용들만 추려보겠다.</p>
<h2 id="변경점"><a href="#변경점" class="headerlink" title="변경점"></a>변경점</h2><h3 id="indent-based-syntax-1주차"><a href="#indent-based-syntax-1주차" class="headerlink" title="indent-based syntax - 1주차"></a>indent-based syntax - 1주차</h3><p>일단 가장 큰 변화는 들여쓰기 문법을 도입하면서 중괄호(<code>{}</code>)를 쓸 필요가 없어졌다는 것이다. 1주차 수업부터 조건문을 설명하면서</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> condition then</span><br><span class="line"> expression</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> expression</span><br></pre></td></tr></table></figure>
<p>처럼 여러 줄의 표현식이 있을 때 중괄호없이 표기하는 예시를 보여준다.</p>
<h3 id="imperative-loop-2주차"><a href="#imperative-loop-2주차" class="headerlink" title="imperative-loop - 2주차"></a>imperative-loop - 2주차</h3><p>지금까지 for문(<code>for comprehension</code>)을 쓸 때, flatMap, map, withFilter 등으로 변환된다고 알고 있었는데 여기에 foreach로 변환되는 문법이 하나 추가되었다. <code>for … do</code>인데</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span></span><br><span class="line"> x <- exp1</span><br><span class="line">do f(x)</span><br><span class="line"><span class="comment">// is equivalent to</span></span><br><span class="line">exp1.foreach(x => f(x))</span><br></pre></td></tr></table></figure>
<p><code>yield</code>처럼 값을 반환하지 않고 실행만 하는 명령식 반복문(imperative loop)이 생겼다.</p>
<h3 id="package-object-3주차"><a href="#package-object-3주차" class="headerlink" title="package-object - 3주차"></a>package-object - 3주차</h3><p>탑레벨(top-level) 변수들이 허용되어서 굳이 패키지 객체가 필요없긴 하지만 기능도 사라졌다.</p>
<h3 id="imports-3주차"><a href="#imports-3주차" class="headerlink" title="imports - 3주차"></a>imports - 3주차</h3><p><code>import</code> 문에서 몇가지 변화가 생겼다. 일단은 패키지 내의 멤버 전부를 가져오는게 <code>import root.from.to._</code>였는데 이제는 <code>import root.from.to.*</code>로 <code>*</code>을 사용하게 되었다. 아직 하위호환으로 <code>_</code>도 사용할 수 있다.</p>
<p>그리고 이름을 변경하여 가져올 때 <code>import from.to.{Pkg => P}</code>였다면 이제는 <code>as</code>라는 키워드를 사용해 <code>import from.to.{Pkg as P}</code>처럼 쓰면 된다.</p>
<p>새로운 <code>given</code> 키워드와 새로운 문법상 맥락이 생기며 <code>given</code> 변수들은 <code>.*</code>로 가져올 수 없으니 given을 한번에 가져오려면 <code>import from.to.given</code>을 쓰거나 given을 포함한 다른 멤버들도 한번에 가져오려면 <code>import from.to{given, *}</code>처럼 사용하면 된다.</p>
<h3 id="Program-Entry-Point-3주차"><a href="#Program-Entry-Point-3주차" class="headerlink" title="Program Entry Point - 3주차"></a>Program Entry Point - 3주차</h3><p>예전에는 Java처럼 <code>main(args: Array[String])</code> 메소드가 있는 Object들을 진입점들로 찾았다면 이제는 <code>@main</code>이 붙은 메소드들이 모두 진입점이 될 수 있다. 그리고 인자로 <code>@main def run(name: String, n: Int)</code>같은 식의 타입을 받으면 받은 인자들을 순서대로 저 타입 변환을 하는데 맞지않으면 실행이 되지 않는다. </p>
<h3 id="Opaque-Types-3주차"><a href="#Opaque-Types-3주차" class="headerlink" title="Opaque Types - 3주차"></a>Opaque Types - 3주차</h3><p>예전에도 데이터의 일관성을 위해 타입을 다른 이름으로 바꾸거나 다른 타입의 인자에 넣거나 trait를 붙여서 구분하는 등의 방식들이 존재했는데, 그 중에서 실행 시점에서 추가적인 리소스를 소모하지 않는 방법은 type alias가 있었지만 원래 타입으로 변환이 가능해서 <code>type UserID = Long</code>, <code>type GroupID = Long</code>이면 두 타입을 혼용하거나 원래 타입과 구분할 수 없다는 단점이 있었고 이걸 해결하기 위해 opaque type이라는 기능이 도입되었다.</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">UserID</span></span>:</span><br><span class="line"> opaque <span class="class"><span class="keyword">type</span> <span class="title">UserID</span> </span>= <span class="type">Long</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">parse</span></span>(string: <span class="type">String</span>) = string.toLongOption</span><br><span class="line"> extension (id: <span class="type">UserID</span>)</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">value</span></span>(id: <span class="type">UserId</span>): <span class="type">Long</span> = id</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">find</span></span>(id: <span class="type">UserID</span>): <span class="type">Option</span>[<span class="type">User</span>] =</span><br><span class="line"> ... (id.value)</span><br></pre></td></tr></table></figure>
<p>이렇게 타입에 이름을 붙이고, 원 타입과의 변환은 선언된 스코프 내에서만 가능해서 type alias와 다르게 안전하게 사용할 수 있다.</p>
<h3 id="Extension-Method-3주차"><a href="#Extension-Method-3주차" class="headerlink" title="Extension Method - 3주차"></a>Extension Method - 3주차</h3><p>위의 예제에서 <code>id.value</code>로 <code>value</code> 멤버가 없는 opaque type에 접근한 것처럼 타입을 확장할 수 있는 기능이다. 예전에는 암묵적 변환(implicit conversion)을 통해 다른 클래스로 변환하고 그 클래스의 메소드를 실행하는 방식이었는데, <code>import</code>를 통해 extension을 가져올 수 있고 특수한 경우로 <code>UserID</code>처럼 opaque type에 연결되어있으면 그 opaque type만 import하면 가져올 수 있다.</p>
<h3 id="Given-5주차"><a href="#Given-5주차" class="headerlink" title="Given - 5주차"></a>Given - 5주차</h3><p>예전에도 Context Bound라는 타입 연산자(<code>:</code>)가 있었고, 풀어서 쓰자면</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">g</span></span>[<span class="type">A</span> : <span class="type">B</span>](a: <span class="type">A</span>)</span><br><span class="line"><span class="comment">// is equivelent to</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">g</span></span>[<span class="type">A</span>](a: <span class="type">A</span>)(<span class="keyword">implicit</span> ev: <span class="type">B</span>[<span class="type">A</span>])</span><br></pre></td></tr></table></figure>
<p>처럼 맥락에 해당하는 묵시적 변수를 <code>implicit</code>으로 표기했는데 이제는 모든 묵시적 행동에 쓰이던 implicit이라는 키워드가 사라지고 이런 용도로는 <code>using</code>으로 쓴다. 그리고 using에서 자동으로 가져오기 위한 변수를 선언하는 키워드는 <code>given</code>이 되었다.</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">object</span> <span class="title">Ordering</span></span>:</span><br><span class="line"> given <span class="type">IntOrd</span>: <span class="type">Ordering</span>[<span class="type">Int</span>] <span class="keyword">with</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">compare</span></span>(x: <span class="type">Int</span>, y: <span class="type">Int</span>) = ...</span><br><span class="line"> given <span class="type">Ordering</span>[<span class="type">Double</span>] <span class="keyword">with</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">compare</span></span>(x: <span class="type">Double</span>, y: <span class="type">Double</span>) = ...</span><br></pre></td></tr></table></figure>
<p>이렇게 해당 given에 이름을 붙일 수도 생략할 수도 있고, <code>given intOrdering: Ordering[Int] = IntOdering</code>처럼 given이 아닌 변수지만 메소드를 제공한다면 given 변수에 할당해서 사용할 수도 있다.</p>
<p>또한 <code>implicitly</code>라고 문맥상 스코프에 존재하는 묵시적인 변수를 가져오는 함수는 이제 <code>summon</code>으로 쓴다. <code>summon[Ordering[Int]]</code>처럼 부르는데, 이것도 <code>implicitly</code>처럼 미리 선언된 함수이다.</p>
<p>given의 경우 다음과 같은 방식으로 가져올 수 있다</p>
<ul>
<li>이름: <code>import Ordering.Int</code></li>
<li>타입: <code>import Ordering.{given Ordering[Int]}</code></li>
<li>타입*: <code>import Ordering.{given Ordering[?]}</code></li>
<li>전부: <code>import Ordering.given</code></li>
</ul>
<p>T 타입의 given은 다음과 같은 순서로 찾는다</p>
<ol>
<li>접근할 수 있는 given 인스턴스들</li>
</ol>
<ul>
<li>상속받았거나 import했거나 스코프 안에서 정의된 변수들</li>
</ul>
<ol start="2">
<li>T와 관련된 컴패니언 객체를 통해서</li>
</ol>
<ul>
<li>‘관련된’의 의미는<ul>
<li>T 자체의 컴패니언 객체</li>
<li>T가 상속하는 타입들의 컴패니언 객체</li>
<li>T에 있는 타입 인자들의 컴패니언 객체</li>
<li>T가 inner class라면 바깥쪽 스코프의 객체</li>
</ul>
</li>
</ul>
<p><code>given a: A</code>가 <code>given b: B</code>보다 더 구체적이다, 라는 말은</p>
<ul>
<li>a가 b보다 가까운 스코프에 있다</li>
<li>b가 정의된 클래스의 스버클래스 안에. a가 있다</li>
<li>a가 b의 서브타입이다</li>
<li>A 타입이 B 타입보다 더 “고정된” 부분이 있다.<ul>
<li><code>Ordering[Int]</code>가 <code>Ordering[?]</code>보다 더 고정되어 있다.</li>
</ul>
</li>
</ul>
<h2 id="유용한-내용"><a href="#유용한-내용" class="headerlink" title="유용한 내용"></a>유용한 내용</h2><p>sbt에 대한 설명은 아주 기본적인 것이나 아주 깊은 내용 아니면 찾기 어려워서 기본적으로 내부에서 사용하는 개념에 대해 정리된 자료를 찾기 힘든데, 3주차의 “sbt, Keys, and Scopes” 챕터에서 sbt 내에서 쓰이는 중요한 개념인 <code>Key</code>와 <code>Task</code>, 그리고 <code>Scope</code>에 대해 잘 설명해준다.</p>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/coursera/" class="post__tag__link">coursera</a></li><li class="post__tag__item"><a href="/tags/Scala/" class="post__tag__link">Scala</a></li></ul><a href="/2021/06/01/after-coursera-epis/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2016-01-02T05:22:28.000Z" class="post__time">January 2, 2016</time><h1 class="post__title"><a href="/2016/01/02/2015-summary/">2015 정리</a></h1></header><div class="post__main echo"><p>남들 다 하니까 해볼 겸, 한번 배워보고 싶지만 매번 미루던 Google Analytics를 다른 방법으로 써볼 겸
이것저것 눌러보며 다른 블로그에서 제공되는 요약 내용을 시도해봤다.</p>
<h3>1. PV</h3>
<p><img src="/images/2015-summary/1.png" alt=""></p>
<ul>
<li>Best Session: 393 (6/15)
<ul>
<li>관련글
<ul>
<li><a href="/2015/06/10/big-data-with-spark-1-2-week/">Spark로 빅데이터 입문, 1-2주차 노트</a> - 6/10</li>
<li><a href="/2015/06/14/big-data-with-spark-3-week/">Spark로 빅데이터 입문, 3주차 노트</a> - 6/14</li>
</ul>
</li>
<li>edX의 <a target="_blank" rel="noopener" href="https://courses.edx.org/courses/BerkeleyX/CS100.1x/1T2015/">Introduction to Big Data with Apache Spark</a>를 들으면서 정리한 노트인데, 내 트위터에 바로 공유했던 1-2주차는 전혀 관심을 받지 못했고, 3주차를 쓰고 하루 뒤에 관련 그룹(페이스북의 <a target="_blank" rel="noopener" href="https://www.facebook.com/groups/sparkkoreauser/">스사모 (한국 스파크 사용자 모임)</a>)에 글을 올리고 나서야 PV가 생겼다.</li>
</ul>
</li>
<li>Best PV: 601 (8/5)
<ul>
<li>관련글
<ul>
<li><a href="/2015/08/04/wtf-1-intro/">WTF - 1. 시작</a> - 8/4</li>
</ul>
</li>
<li>글 4개 시리즈를 동시에 올렸더니 고유 접속자(DAU)는 낮아도 PV가 높게 나왔다.</li>
</ul>
</li>
</ul>
<p>두 시리즈의 공통점은 유의미한 피드백이 전혀 없었다는 점.</p>
<h3>2. Top 7</h3>
<p><img src="/images/2015-summary/2.png" alt=""></p>
<p>왜 7이냐면 Top Page(<code>/</code>)와 블로그 메인(<code>/blog</code>)과 광고 페이지를 제외하니 첫 페이지에서 7개가 남아서.</p>
<ol>
<li><a href="/2012/10/09/getting-cozy-with-underscore-js/">underscore.js로 편해지자</a></li>
<li><a href="/2015/06/10/big-data-with-spark-1-2-week/">Spark로 빅데이터 입문, 1-2주차 노트</a></li>
<li><a href="/2015/01/18/pis-review/">책 후기, Programming in Scala 2nd</a></li>
<li><a href="/2015/05/28/callback-to-future-functor-applicative-monad/">Callback에서 Future로(그리고 Functor, Monad)</a></li>
<li><a href="/2015/08/04/wtf-1-intro/">WTF - 1. 시작</a></li>
<li><a href="/2012/09/19/10-good-reasons-use-coffeescript/">커피스크립트를 쓰면 좋은 10가지 이유</a></li>
<li><a href="/2015/06/14/big-data-with-spark-3-week/">Spark로 빅데이터 입문, 3주차 노트</a></li>
</ol>
<h3>3. Referrer</h3>
<p><img src="/images/2015-summary/3.png" alt=""></p>
<p>역시 구글 검색이 제일 많고, 두번째는 직접 방문, 세번째는 광고라 필터링했고 네번째는 트위터 공유. 그리고
readtrend에서 공유된 글은 시간이 지나도 방문자가 꾸준히 들어오는게 신기하다. 페이스북은 어디서
공유되었는지 알 길이 없어서 막을 수 있으면 막고 싶지만 방법이 있어도 귀찮아서 안할 것 같다.</p>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/annual/" class="post__tag__link">annual</a></li></ul><a href="/2016/01/02/2015-summary/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-08-10T09:34:02.000Z" class="post__time">August 10, 2015</time><h1 class="post__title"><a href="/2015/08/10/elm-resources/">Elm Reousrces</a></h1></header><div class="post__main echo"><p>얼마전에 Haskell 책을 읽었더니, 예전에 관심은 있었지만 어디서부터 접근해야할지
몰라서 미뤄뒀던 <a target="_blank" rel="noopener" href="http://elm-lang.org/">Elm</a>이 생각났다. 어떤 것이라고 정리할 정도로 아직 잘 알지
못해서 또 미뤄두고 있었는데, 혹시나 관심을 가지고 있는 분이 있다면 함께
공부할 수 있었으면 좋겠다는 생각에 어떤 점에서 관심을 가지게 되었고 어떤 것을
봐왔는지 정리해두려고 한다.</p>
<h2>time travel debugger</h2>
<p>요새 react-hot-loader를 통해 hot swap(코드 수정 후 현재 상태까지 다시 컨트롤하지
않고 그 상태로 바로 반영하는 것)이 가능하다고 사람들이 환호했는데, 그 이전에
Clojurescript의 Leiningen plugin으로 <a target="_blank" rel="noopener" href="https://github.com/bhauman/lein-figwheel">lein-figwheel</a>이 있었다.</p>
<div class="center">
<iframe width="560" height="315" frameborder="0" allowfullscreen
src="https://www.youtube.com/embed/j-kj2qwJa_E"></iframe>
</div>
<p>하지만 여기에서 조금 더 발전한 <a target="_blank" rel="noopener" href="http://debug.elm-lang.org/">time travel debugger</a>가 이미 있었다.
자세한 내용은 당시 <a target="_blank" rel="noopener" href="https://news.ycombinator.com/item?id=7593032">Hacker News</a>의 코멘트를 참고.</p>
<div class="center">
<iframe width="560" height="315" frameborder="0" allowfullscreen
src="https://www.youtube.com/embed/zybahE0aQqA"></iframe>
</div>
<div class="center">
<iframe width="560" height="315" frameborder="0" allowfullscreen
src="https://www.youtube.com/embed/vS3yzUo7l8Y"></iframe>
</div>
<p>이게 가능한 것은 Elm의 runtime에 <a target="_blank" rel="noopener" href="http://elm-lang.org/guide/reactivity">Signal</a>이라는 흐름 위에서 돌아가도록
설계되어있어서 그런 것이라 추정한다. (아직 뜯어보지 않았다.)</p>
<h2>blazing fast</h2>
<p>vDOM/react의 구현체 속도 벤치마크가 유행했던 적이 있는데 거기에서 뜬금없이
등장한 적이 있다. 홈페이지에도 <a target="_blank" rel="noopener" href="http://elm-lang.org/blog/blazing-fast-html">자랑</a>이 올라와있는데 벤치마크 결과는
다음과 같다. 참고로 <a target="_blank" rel="noopener" href="https://github.com/omcljs/om">Om</a>은 Clojurescript의 React 구현체다.</p>
<p><img src="/images/elm-resources/1.png" alt=""></p>
<h2>tutorial</h2>
<p>공식 홈페이지에도 문서화나 예제가 잘 되어있긴 하지만 단계별로 따라갈만한 과정은
아직 없다. 그래서 검색하다가 유튜브의 <a target="_blank" rel="noopener" href="https://www.youtube.com/playlist?list=PLtdCJGSpculbDT_p4ED9oLTJQrzoM1QEL">Elm Tutorial</a>을 발견했는데,
문제는 이게 0.12 기준이라 현재(0.15.1)와 맞지 않다. 그래도 유일하게 존재하는
튜토리얼이라 공식 문서를 검색해가며 따라해봤고, 단계별로 소스를
<a href="repo">정리</a>해봤지만 실시간으로 저장하며 따라한 것이라 따라가기 벅찰 때는
갑자기 단계가 휙 뛸 수도 있다. (그러므로 언제든 더 좋은 정리를 PR로!)</p>
<p>이 튜토리얼을 끝내고 얼마 뒤 유료로 <a target="_blank" rel="noopener" href="https://pragmaticstudio.com/elm">Elm: Building Reactive Web Apps</a>라는
유료 강의가 올라왔는데, 들어보지않아서 어떤 강의인지는 잘 모르겠다.</p>
<h2>other drugs</h2>
<ul>
<li><a target="_blank" rel="noopener" href="https://www.classes.cs.uchicago.edu/archive/2015/winter/22300-1/">Functional Programming</a>
<ul>
<li>시카고대에서 올해 초 겨울학기 강의를 Elm으로 진행했다. 조만간 읽을 예정.</li>
<li>0.14 기준이라 <a target="_blank" rel="noopener" href="http://elm-lang.org/blog/announce/0.15#introducing-mailboxes">Mailbox와 Task</a> 대신 Channel이 있어서 지금 그대로
따라하기는 어렵다.</li>
</ul>
</li>
<li><a target="_blank" rel="noopener" href="https://gist.github.com/ohanhi/0d3d83cf3f0d7bbea9db">Learning FP the hard way: Experiences on the Elm language</a>
<ul>
<li>Gist에 한페이지짜리 함수형 프로그래밍 소개글이 있지만 이것만으로 이해하기는
어려워보인다.</li>
</ul>
</li>
<li><a target="_blank" rel="noopener" href="https://presentate.com/rtfeldman/talks/shipping-a-production-web-app-in-elm">Shipping a Production Web App in Elm</a>
<ul>
<li>Dreamwriter라는 in-browser editor가 원래 React/Flux + CoffeeScript로
만들어졌는데, 속도와 관리 때문에 Elm으로 넘어간 이야기.</li>
</ul>
</li>
<li><a target="_blank" rel="noopener" href="https://gist.github.com/mgold/461dbf37d4d34767e5da">Using Mailboxes in Elm</a>
<ul>
<li>Actor Model처럼 Mailbox라는 개념을 통해서 Message를 보내는데 문서가 부족해서
누가 대신 쓴 글</li>
</ul>
</li>
<li><a target="_blank" rel="noopener" href="http://xgrommx.github.io/rx-book/content/similar_libraries/index.html#elm">Similar libraries which were inspired by RxJS: Elm</a>
<ul>
<li>rx-book의 한 챕터에 링크를 정리해놨는데 어떤 Elm 커뮤니티보다 잘해놨다.</li>
</ul>
</li>
<li><a target="_blank" rel="noopener" href="https://pragprog.com/book/7lang/seven-more-languages-in-seven-weeks">Seven More Languages in Seven Weeks</a>
<ul>
<li>"You’ll learn seven exciting languages: Lua, Factor, Elixir, Elm, Julia, MiniKanren, and Idris."</li>
<li>유일하게 Elm을 언급한 책인데 전작인 <a target="_blank" rel="noopener" href="http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-185-7">세븐 랭귀지</a>처럼 번역될 것 같아서 일단 기다리는 중.</li>
</ul>
</li>
</ul>
<hr>
<p>간단하게 링크만 올릴 예정이었는데 쓰다보니 아직 제대로 이해하지 못한 불필요한
설명이 길어졌다. 하지만 아까워서 삭제하지 않고 그냥 발행.</p>
<style type="text/css">
.center { text-align: center; }
</style>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/Elm/" class="post__tag__link">Elm</a></li></ul><a href="/2015/08/10/elm-resources/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-08-04T13:58:10.000Z" class="post__time">August 4, 2015</time><h1 class="post__title"><a href="/2015/08/04/wtf-4-parser/">WTF - 4. 모나드 분자식 파서(Monadic Molecule Parser)</a></h1></header><div class="post__main echo"><blockquote>
<p>What is the Functional?</p>
<ol>
<li><a href="/2015/08/04/wtf-1-intro/">Introduction</a></li>
<li><a href="/2015/08/04/wtf-2-adt/">Algebraic Data Type</a></li>
<li><a href="/2015/08/04/wtf-3-fam/">Maybe or Not</a></li>
<li><a href="/2015/08/04/wtf-4-parser/">Monadic Molecule Parser</a></li>
</ol>
</blockquote>
<p>Codewars에 분자식(문자열)에서 원자들이 몇 개인지 세는 문제가 있었다. 이미
제출한 답은 다음과 같은 구조였다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> token = <span class="function">(<span class="params">str</span>) =></span> str.match(regToken);</span><br><span class="line"><span class="keyword">const</span> lexer = <span class="function">(<span class="params">arr</span>) =></span> arr.reduce(<span class="function">(<span class="params">r,t</span>) =></span> ..., [])</span><br><span class="line"><span class="keyword">const</span> parse = <span class="function">(<span class="params">form</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> syms = lexer(token(formula));</span><br><span class="line"> <span class="keyword">let</span> offset = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> buffer = traverse();</span><br><span class="line"> <span class="keyword">return</span> evaluate(buffer);</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">evaluate</span>(<span class="params">array</span>) </span>{...}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>옛날에 컴파일러 수업을 들었던 게 대충 생각나서 그래도 tokenizer로 식을 token
단위로 끊고, lexer로 symbol로 만들고(원래는 scan할 때 한 번에 하는 작업이었던
것으로 기억하지만), parse에서 Parse Tree를 만들어서 evaluator에서
실행(계산)하도록 만들려고 짜다가 구현을 할수록 구조는 복잡해지는데 비해 난이도는
올라가서 그냥 뒷부분은 한군데 섞어버렸다. 배운 걸 써먹기 위해서 이걸 다시
Haskell로 도전해보았다. 일단 문제를 단순화시켜서 괄호 없는 분자식을 구현해보자.
규칙은 다음과 같다.</p>
<ol>
<li>원소기호는 대분자와 소문자, 혹은 대문자로 이루어진다.</li>
<li>개수는 원소기호 뒤에 오는 0자리 이상의 숫자로 없다면 1이 된다.</li>
<li>원소기호와 개수를 합쳐서 동원소분자(homoatomic; 물론 제대로 된 명칭인지는
모름)라고 부른다.</li>
<li>분자식은 동원소분자가 1개 이상 이어진다.</li>
</ol>
<p>BNF라는 명칭만 기억나고 정확한 내용은 기억나지 않지만 비슷하게 옮겨보자면</p>
<ul>
<li>molecules = homoatomic | molecules</li>
<li>homoatomic = atom amount | atom</li>
</ul>
<p>이제 이걸 적용할 수 있는 파서를 만들어보자. 일단 파서의 적절한 타입을
만들어야 하는데, 함수형에서는 어떤 흐름을 기록하는데 자주 쓰이는
<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Monad_(functional_programming)#State_monads">State</a>라는
패턴이 있다. 모양은 대충 <code>type State s a = s -> (a, s)</code>가 된다. 즉, 현재의 어떤
상태<code>s</code>에서 그 상태에서 원하는 결과 <code>a</code>와 다음 상태<code>s</code>를 동시에 가져오게 된다.
상태는 변이(tramsform)되지만 원하는 값을 얻으면서 변수의 불변성(immutability)도
만족해서, 메모리만 충분하다면 구조에 따라 상태 변이를 기록할 수 있어서 추적하기
쉽다는 장점이 있다. 다시 돌아와서, State처럼 파싱할 문자열을 받아서 어떤 연산을
규칙에 맞는 결과물 <code>T</code>를 가져온 뒤 남은 문자열과 함께 돌려주는 일종의 State같은
타입을 Parser라고 정의해서 사용할 것이다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// type Parser[T] = String -> (T, String)</span></span><br></pre></td></tr></table></figure>
<p>먼저 앞에서 구현했던 것처럼 token을 가져오는 Parser를 만들어보자. Haskell의
경우에는 강한 타입에다 패턴 매칭을 지원하는 언어라서 원하는 타입이나 원하는
형태가 아니면 실패했다고 돌려줄 수 있지만, JavaScript는 둘 다 지원되지 않아서
내가 원하는 것이 맞는지 글자를 소모하지 않고 확인할 수 있는 기능과 글자를
소모해서 원하는 것인지 확인하는 기능 두 가지를 통해 tokenizer를 구현할 수 있다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// Parser => Parser</span></span><br><span class="line"><span class="keyword">const</span> ahead = <span class="function"><span class="params">p</span> =></span> <span class="function">(<span class="params">str</span>) =></span> p(str) ? [<span class="string">''</span>, str] : [];</span><br><span class="line"><span class="comment">///// Parser => Parser</span></span><br><span class="line"><span class="keyword">const</span> satisfy = <span class="function"><span class="params">p</span> =></span> <span class="function">(<span class="params">str</span>) =></span> p(str) ? [str[<span class="number">0</span>], str.substr(<span class="number">1</span>)] : [];</span><br></pre></td></tr></table></figure>
<p>실패<code>[]</code>의 경우는 같고, 성공할 경우에 <code>ahead</code>는 아무것도 매칭하지 않은 채
원래의 문자열<code>str</code>을 돌려주고, <code>satisfy</code>의 경우 매칭된 글자와 나머지 문자열을
돌려준다. 원래는 튜플(Tuple)로 구현하는 게 좋지만, JavaScript에 그런 게
있을 리가.</p>
<p>이제 다음 글자가 대문자라는 파서를 만들려면 다음과 같이 만들면 된다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> upper = satisfy(<span class="function"><span class="params">ch</span> =></span> <span class="string">'A'</span> <= ch[<span class="number">0</span>] && ch[<span class="number">0</span>] <= <span class="string">'Z'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log( upper(<span class="string">"H"</span>) ); <span class="comment">// [ 'H', '' ]</span></span><br></pre></td></tr></table></figure>
<p>하지만 타입 확인도 패턴 매칭도 존재하지 않아서 적절한 입력인지 확인할 수 있는
규칙과 그것을 합성할 수 있는 기능이 필요하다. 물론 합성 기능은 다음 글자가
소문자라는 파서를 만들었을 때 그 파서와 합성하는 데 사용할 수도 있다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// (Parser, Parser) -> Parser</span></span><br><span class="line"><span class="keyword">const</span> and = <span class="function">(<span class="params">pa, pb</span>) =></span> <span class="function">(<span class="params">str</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> ra = pa(str);</span><br><span class="line"> <span class="keyword">if</span>(ra.length === <span class="number">0</span>) <span class="keyword">return</span> [];</span><br><span class="line"> <span class="keyword">const</span> rb = pb(ra[<span class="number">1</span>]);</span><br><span class="line"> <span class="keyword">if</span>(rb.length === <span class="number">0</span>) <span class="keyword">return</span> [];</span><br><span class="line"> <span class="keyword">return</span> [ra[<span class="number">0</span>] + rb[<span class="number">0</span>], rb[<span class="number">1</span>]];</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>이제 첫 번째 파서가 성공할 경우 <code>[match1, rest1]</code>를 받아서 <code>rest1</code>을 다음 파서에
넘겨서 <code>[match2, rest2]</code>를 가져온 뒤에 <code>[match1 + match2, rest2]</code>라는 결과를
만들어주는 파서를 생성할 수 있게 되었다. 물론 둘 중 하나라도 실패하면 실패<code>[]</code>가
리턴된다. 이걸 사용해서 소문자 확인과 결합해보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> lower = satisfy(<span class="function"><span class="params">ch</span> =></span> <span class="string">'a'</span> <= ch[<span class="number">0</span>] && ch[<span class="number">0</span>] <= <span class="string">'z'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(and(upper, lower)(<span class="string">"Mg"</span>)); <span class="comment">// [ 'Mg', '' ]</span></span><br></pre></td></tr></table></figure>
<p>그리고 앞서 만들어놓은 <code>ahead</code>와 더불어 원하는 조건인지 확인을 합성할 수도 있다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> chr = ahead(<span class="function"><span class="params">str</span> =></span> str.length > <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> upper = and(chr, satisfy(<span class="function"><span class="params">ch</span> =></span> <span class="string">'A'</span> <= ch[<span class="number">0</span>] && ch[<span class="number">0</span>] <= <span class="string">'Z'</span>));</span><br><span class="line"><span class="keyword">const</span> lower = and(chr, satisfy(<span class="function"><span class="params">ch</span> =></span> <span class="string">'a'</span> <= ch[<span class="number">0</span>] && ch[<span class="number">0</span>] <= <span class="string">'z'</span>));</span><br><span class="line"><span class="keyword">const</span> digit = and(chr, satisfy(<span class="function"><span class="params">ch</span> =></span> <span class="string">'0'</span> <= ch[<span class="number">0</span>] && ch[<span class="number">0</span>] <= <span class="string">'9'</span>));</span><br></pre></td></tr></table></figure>
<p>다음 글자가 존재하는지, 존재할 때 원하는 범위의 글자가 맞는지를 한 번에 확인할
수 있는 파서들이 만들어졌다. 이제 원소기호를 파싱할 때 대문자와 소문자가
연속으로 올 때도 있지만, 대문자만 존재할 수 있으니 <code>or</code>를 만들어보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// (Parser, Parser) -> Parser</span></span><br><span class="line"><span class="keyword">const</span> or = <span class="function">(<span class="params">pa, pb</span>) =></span> <span class="function">(<span class="params">str</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> r = pa(str);</span><br><span class="line"> <span class="keyword">if</span>(r.length > <span class="number">0</span>) <span class="keyword">return</span> r;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">return</span> pb(str);</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p><code>and</code>처럼 파서 두 개를 받아서 새로운 파서를 만들어주는데, 차이는 첫 번째 파서가
실패하면 바로 실패했다는 결과를 넘겨주고 뒤의 규칙은 확인하지 않으며, 두 번째
파서가 실패할 경우에는 그 자체가 실패의 결괏값이므로 그냥 넘겨주면 된다. 이걸로
원소기호를 가져와 보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> atom = or(and(upper, lower), upper);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(atom(<span class="string">"Mg"</span>)); <span class="comment">// [ 'Mg', '' ]</span></span><br><span class="line"><span class="built_in">console</span>.log(atom(<span class="string">"H"</span>)); <span class="comment">// [ 'H', '' ]</span></span><br></pre></td></tr></table></figure>
<p>이제 둘 다 만족하게 되었다. 그럼 다음은 숫자를 0개 이상 받을 때인데, 재귀적인
규칙이라 앞에서 구현한 것들보다 약간 복잡해진다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> amount = or(and(digit, amount), digit);</span><br></pre></td></tr></table></figure>
<p>ES6에서 생긴 상수/변수인 <code>const</code>와 <code>let</code>은 호이스팅(hoisting)되지 않아서 이렇게
만들면 작동하지 않는다. 그래서 함수가 실행된 뒤에 <code>amount</code>를 읽을 수 있도록
소스가 약간 길어지지만 감수하면서 만들자면 다음과 같다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> amount = <span class="function">(<span class="params">str</span>) =></span> or(and(digit, amount), digit)(str);</span><br></pre></td></tr></table></figure>
<p>하지만 0개 이상을 읽는 경우가 이번뿐이 아니므로 이걸 좀 다듬어보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> many = <span class="function">(<span class="params">p</span>) =></span> <span class="function">(<span class="params">str</span>) =></span> or(and(p, many(p)), p)(str);</span><br><span class="line"><span class="keyword">const</span> amount = many(digit);</span><br></pre></td></tr></table></figure>
<p>이제 <code>many</code>를 사용하면 나머지를 완성할 수 있다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> homo = or(and(atom, amount), atom);</span><br><span class="line"><span class="keyword">const</span> mole = many(homo);</span><br></pre></td></tr></table></figure>
<p>원소기호와 개수가 나오는 경우 혹은 원소기호만 있는 경우가 0번 이상 반복되는 것을
분자식이라고 한다. 아예 받지 않거나 실패하는 경우에는 아무것도 출력하지 않도록
결과를 다듬어보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> parse = <span class="function">(<span class="params">p</span>) =></span> <span class="function">(<span class="params">str</span>) =></span> or(p, <span class="function">(<span class="params">str</span>) =></span> [<span class="string">''</span>, str])(str)[<span class="number">0</span>];</span><br><span class="line"><span class="built_in">console</span>.log(parse(mole)(<span class="string">"H2O"</span>)); <span class="comment">// H2O</span></span><br></pre></td></tr></table></figure>
<p>이제 <code>satisfy(ch=>ch=='(')</code> 같은 식으로 열고 닫는 괄호를 3종류로 나누어서 규칙을
만들고 <code>and</code>와 <code>or</code>로 잘 조합하면 원래 문제의 답을 얻을 수 있을 것이다. 맨 위에
적어놓은 것보다 훨씬 깔끔한 구조로 함수들을 연결해서 답을 만들어냈다. 다만 현재
JavaScript 엔진들은 스코프를 생성하는 비용이 비싸서 함수 호출이 많아질수록 꽤
느려질 수 있다. 다만 불변성 구조가 유행하고 있고 엔진들도 최적화가 많이 진행되고
있으므로 trade-off를 통해 성능은 비슷하지만 구조는 깔끔하게 만들 수 있지 않을까
기대한다.</p>
<p>예전에 Rx, LINQ 등을 만든 에릭 마이어(Erik Meijer)가 edX에서 함수형 프로그래밍
입문을 강의한다길래 반가운 마음에 들어봤는데, 이상한 아저씨가 이상한 배경에
하이톤으로 함수형에 대해 역사부터 열심히 설명하길래 거기까지만 듣고 포기했던
적이 있다. 이번에 읽은 책이 Haskell 자체보다는 함수형 개념에 관해 설명한 책이라
Haskell로 문제를 풀어보면서 모르는 게 있어서 리뷰하려고 다시 들어봤더니 여전히
그 배경은 적응이 안 되지만 그래도 친절하고 좋은 강의였다(참고로 아래의 Contents
링크에 들어가면 화질별 강의와 슬라이드, 자막이 있다). 10월 15일에 다음 강의가
열리는데, 함수형이나 Haskell 입문에 관심 있는 분들께는 시각적인 어려움을
참작하고라도 매우 도움이 되는 강의니 추천한다.</p>
<p>ps, 구직중</p>
<hr>
<p>Reference</p>
<ul>
<li>
<p><a target="_blank" rel="noopener" href="https://www.cs.nott.ac.uk/~gmh/monparsing.pdf">Monadic Parser Combinators - Graham Hutton, Erik Meijer</a></p>
</li>
<li>
<p>Codewars</p>
<ul>
<li><a target="_blank" rel="noopener" href="http://www.codewars.com/kata/molecule-to-atoms">Molecule to atoms</a></li>
</ul>
</li>
<li>
<p>Hackage: Official Haskell Package archive</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://hackage.haskell.org/package/base-4.8.1.0/docs/Text-ParserCombinators-ReadP.html">Text.ParserCombinators.ReadP</a></li>
</ul>
</li>
<li>
<p>Haskell/Wikibooks</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://en.wikibooks.org/wiki/Haskell/ParseExps">Haskell/ParseExps</a></li>
</ul>
</li>
<li>
<p>FP101x</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://www.edx.org/course/introduction-functional-programming-delftx-fp101x-0">edX course 10/15~</a></li>
<li><a target="_blank" rel="noopener" href="https://github.com/fptudelft/FP101x-Content">Contents</a></li>
</ul>
</li>
</ul>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/functional/" class="post__tag__link">functional</a></li><li class="post__tag__item"><a href="/tags/monad/" class="post__tag__link">monad</a></li></ul><a href="/2015/08/04/wtf-4-parser/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-08-04T13:58:04.000Z" class="post__time">August 4, 2015</time><h1 class="post__title"><a href="/2015/08/04/wtf-3-fam/">WTF - 3. Maybe or Not</a></h1></header><div class="post__main echo"><blockquote>
<p>What is the Functional?</p>
<ol>
<li><a href="/2015/08/04/wtf-1-intro/">Introduction</a></li>
<li><a href="/2015/08/04/wtf-2-adt/">Algebraic Data Type</a></li>
<li><a href="/2015/08/04/wtf-3-fam/">Maybe or Not</a></li>
<li><a href="/2015/08/04/wtf-4-parser/">Monadic Molecule Parser</a></li>
</ol>
</blockquote>
<h2>Maybe</h2>
<p>바로 앞에서 언급했듯이 최근에 생겼거나 메이저 업데이트를 한 언어들이라면 대부분
지원하는 Maybe(Optional, Option)라는 타입이 있다. 값을 가지고 있는 Just라는
타입과 값이 없는 Nothing이라는 타입 중 하나가 되는 섬 타입이다. 일단 함수형이니
하는 이야기는 잠시 미뤄두고 간단하게 Maybe를 만들어보자. Maybe의 정의를 간단하게
표현해보자면 다음과 같다.</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">data</span> <span class="type">Maybe</span> a = <span class="type">Just</span> a | <span class="type">Nothing</span></span></span><br></pre></td></tr></table></figure>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Maybe</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">toString</span>(<span class="params"></span>)</span> { <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">"Must be implemented."</span>); }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Just</span> <span class="keyword">extends</span> <span class="title">Maybe</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">constructor</span>(<span class="params">v</span>)</span> {</span><br><span class="line"> <span class="built_in">super</span>();</span><br><span class="line"> <span class="built_in">this</span>.value = v;</span><br><span class="line"> <span class="built_in">Object</span>.freeze(<span class="built_in">this</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="title">toString</span>(<span class="params"></span>)</span> { <span class="keyword">return</span> <span class="string">`Just <span class="subst">${<span class="built_in">this</span>.value.toString()}</span>`</span>; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Nothing</span> <span class="keyword">extends</span> <span class="title">Maybe</span> </span>{</span><br><span class="line"> <span class="function"><span class="title">constructor</span>(<span class="params">v</span>)</span> {</span><br><span class="line"> <span class="built_in">super</span>();</span><br><span class="line"> <span class="built_in">Object</span>.freeze(<span class="built_in">this</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="title">toString</span>(<span class="params"></span>)</span> { <span class="keyword">return</span> <span class="string">"Nothing"</span>; }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> nothing = <span class="keyword">new</span> Nothing();</span><br></pre></td></tr></table></figure>
<p>정의는 단순하지만 같은 내용을 JavaScript로 구현하면 다소 길어진다. 소스를 보면
알겠지만, Just는 생성될 때만 값을 받을 수 있고 생성된 후에는 값을 변경할 수
없다. 이제 이 Maybe를 어떻게 다룰지에 대해서 생각을 해보자. Maybe 타입을 통해
어떤 연산을 하고 싶을 때 메소드를 추가해서 Maybe를 계속 생산하도록 만들면
편하겠지만, 값이 있다 없다의 속성을 가질 수 있다면 Maybe의 연산 결과를 Maybe라고
유지하고, 값이 없을 때는 계속 값이 없도록 유지하려면 그 결괏값을 보장해줘야한다.
이걸 만족하는 연산들을 생각해보자.</p>
<ol>
<li>값을 Maybe로 감싸서 새로운 Maybe를 만들어준다.</li>
<li>Maybe의 값에 그 값을 처리하는 함수를 적용하고 싶다.</li>
<li>그런데 그 함수가 Maybe의 값일 수도 있다.</li>
<li>함수의 결괏값 자체가 Maybe라면 어떨까?</li>
</ol>
<p>1번의 구현은 간단하다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// unit : a -> Maybe a</span></span><br><span class="line"><span class="keyword">const</span> unit = <span class="function">(<span class="params">x</span>) =></span> <span class="keyword">new</span> Just(x)</span><br></pre></td></tr></table></figure>
<p>값만 존재할 때는 두 배로 만들려면 단순히 <code>x * 2</code>를 하면 되지만, Maybe로 감싸져
있으니 바로 적용하기 어렵다. 그러니 2번처럼 값을 처리하는 함수를 적용할 수 있는
기능을 구현해보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> isNothing = <span class="function">(<span class="params">m</span>) =></span></span><br><span class="line"> m.constructor.name === <span class="string">"Nothing"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">///// fmap : Maybe a, (a -> b) -> Maybe b</span></span><br><span class="line"><span class="keyword">const</span> fmap = <span class="function">(<span class="params">m, fn</span>) =></span></span><br><span class="line"> isNothing(m)</span><br><span class="line"> ? <span class="keyword">new</span> Nothing</span><br><span class="line"> : <span class="keyword">new</span> Just(fn(m.value))</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> doub = <span class="function">(<span class="params">d</span>) =></span> d * <span class="number">2</span></span><br><span class="line"><span class="built_in">console</span>.log( fmap(unit(<span class="number">1</span>), doub).toString() ); <span class="comment">// Just 2</span></span><br><span class="line"><span class="built_in">console</span>.log( fmap(nothing, doub).toString() ); <span class="comment">// Nothing</span></span><br></pre></td></tr></table></figure>
<p>Maybe의 값이 함수일 경우에 그 함수를 다른 Maybe의 값에 적용해보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// appl : Maybe (a -> b), Maybe a -> Maybe b</span></span><br><span class="line"><span class="keyword">const</span> appl = <span class="function">(<span class="params">mfn, ma</span>) =></span></span><br><span class="line"> isNothing(mfn) || isNothing(ma)</span><br><span class="line"> ? <span class="keyword">new</span> Nothing()</span><br><span class="line"> : unit(mfn.value(ma.value))</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> mdoub = unit(doub);</span><br><span class="line"><span class="built_in">console</span>.log( appl(mdoub, nothing).toString() ); <span class="comment">// Nothing</span></span><br><span class="line"><span class="built_in">console</span>.log( appl(mdoub, unit(<span class="number">1</span>)).toString() ); <span class="comment">// Just 2</span></span><br></pre></td></tr></table></figure>
<p>그런데 모양을 보면 <code>fmap</code>과 비슷해서 <code>fmap</code>을 재사용해서 구현할 수도 있다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> appl2 = <span class="function">(<span class="params">mfn, ma</span>) =></span></span><br><span class="line"> isNothing(mfn)</span><br><span class="line"> ? <span class="keyword">new</span> Nothing()</span><br><span class="line"> : fmap(ma, mfn.value)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log( appl2(mdoub, nothing).toString() ); <span class="comment">// Nothing</span></span><br><span class="line"><span class="built_in">console</span>.log( appl2(mdoub, unit(<span class="number">1</span>)).toString() ); <span class="comment">// Just 2</span></span><br></pre></td></tr></table></figure>
<p>이제 마지막으로 함수의 결과 자체가 Maybe일 경우를 생각해보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// bind : Maybe a, (a -> Maybe b) -> Maybe b</span></span><br><span class="line"><span class="keyword">const</span> bind = <span class="function">(<span class="params">ma, fn</span>) =></span></span><br><span class="line"> isNothing(ma)</span><br><span class="line"> ? <span class="keyword">new</span> Nothing()</span><br><span class="line"> : fn(ma.value)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> udoub = <span class="function">(<span class="params">d</span>) =></span> unit(doub(d)); <span class="comment">// a -> Maybe b</span></span><br><span class="line"><span class="built_in">console</span>.log( bind(nothing, udoub).toString() ); <span class="comment">// Nothing</span></span><br><span class="line"><span class="built_in">console</span>.log( bind(unit(<span class="number">1</span>), udoub).toString() ); <span class="comment">// Just 2</span></span><br></pre></td></tr></table></figure>
<p>지금까지의 구현에서 JavaScript 자체의 복잡한 기능을 사용한 곳은 없다. 구현
자체가 어렵지도 않고 짧아서 여기까지는 다들 이해할 수 있을 것으로 생각한다.
그런데 안에 값을 넣을 수 있는 타입 중에서 개발자들이 항상 사용하고 있으며,
다들 사용법에 대해 아주 잘 알고 있는 타입이 하나 있다. 이제 Maybe를 <code>Array</code>와
비교해보자.</p>
<h2>F, A, M with Array</h2>
<h3>1. Functor</h3>
<p>앞에서 구현했던 <code>fmap</code>에서 설명을 돕기 위해 구현 위에 주석으로 타입을 적어놓은
것이 있다. 처음에는 Haskell 식으로 타입을 적었다가 이해하기 편하도록 수정했더니
무슨 언어인지 모를 내용이 되긴 했지만.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// fmap : Maybe a, (a -> b) -> Maybe b</span></span><br></pre></td></tr></table></figure>
<p>값을 가지고 있는 타입과 값을 변환하는 함수를 받아서 다른 값을 가지고 있는
타입으로 변환해준다. 이걸 이해하기 좋게 조금 수정해보자면,</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// amap : Array a, (a -> b) -> Array b</span></span><br><span class="line"><span class="keyword">const</span> amap = <span class="function">(<span class="params">arr, fn</span>) =></span> arr.map(fn);</span><br><span class="line"><span class="built_in">console</span>.log( amap([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>], (<span class="function"><span class="params">n</span> =></span> <span class="built_in">String</span>(n))) ); <span class="comment">// [ '1', '2', '3' ]</span></span><br></pre></td></tr></table></figure>
<p>a라는 타입의 값을 가지고 있는 어떤 타입을 ⓐ라고 하고, b의 경우를 ⓑ라고 하면,
(a -> b) 함수를 통해 결과적으로 (ⓐ -> ⓑ)를 만족하도록 연산할 수 있는 타입을
Functor라고 부른다. Array에서는 그런 연산을 해주는 <code>map</code> 메소드를 가지고 있다.</p>
<h3>2. Applicative Functor</h3>
<p>순서대로 <code>fmap</code> 다음에 구현했던 <code>appl</code>을 이야기할 차례다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// appl : Maybe (a -> b), Maybe a -> Maybe b</span></span><br></pre></td></tr></table></figure>
<p>applicative라는 표현 그대로 어딘가에 적용할 수 있는 Functor이다. 즉, 함수를
가지고 있는 Functor.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> doub = <span class="function"><span class="params">d</span> =></span> d * <span class="number">2</span>;</span><br><span class="line"><span class="keyword">const</span> incr = <span class="function"><span class="params">d</span> =></span> d + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">///// Array (a -> b), Array a -> Array b</span></span><br><span class="line"><span class="keyword">const</span> appl = <span class="function">(<span class="params">fns, <span class="keyword">as</span></span>) =></span> fns.map(<span class="function"><span class="params">fn</span> =></span> <span class="keyword">as</span>.map(fn))</span><br><span class="line"> .reduce(<span class="function">(<span class="params">r,b</span>) =></span> r.concat(b), [])</span><br><span class="line"><span class="built_in">console</span>.log(</span><br><span class="line"> appl( [doub, incr], [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>] )</span><br><span class="line">); <span class="comment">// [ 2, 4, 6, 2, 3, 4 ]</span></span><br></pre></td></tr></table></figure>
<p>이렇게 (a -> b)를 가지고 있는 Array와 Array a를 통해 Array b를 만들었다. 위에서
말했듯 함수를 가지고 있는 Functor(Array (a -> b))와 다른 Functor(Array a)를 통해
다른 Functor(Array b)를 만들어내는 Applicative Functor를 <code>map</code>을 사용해서 간단히
구현해보았다. 하지만 Applicative Functor 자체를 본 적이 별로 없어서 내가 맞게
이해하고 있는 것인지 잘 모르겠다.</p>
<h3>3. Monad</h3>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///// bind : Maybe a, (a -> Maybe b) -> Maybe b</span></span><br></pre></td></tr></table></figure>
<p>JavaScript에서는 <a target="_blank" rel="noopener" href="https://lodash.com/">lodash</a>같은 라이브러리를 사용하지 않은
사람에게 익숙하지 않은 개념일 수 있지만, 다른 함수형 언어들을 써본 사람이라면
Sequence 종류에서 기본적으로 지원해주는 익숙한 개념이 있다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> flatMap = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.map(fn).reduce(<span class="function">(<span class="params">r,a</span>) =></span> r.concat(a), [])</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(</span><br><span class="line"> [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]::flatMap(<span class="function"><span class="params">d</span> =></span> <span class="built_in">Array</span>(d).fill(d))</span><br><span class="line">); <span class="comment">// [ 1, 2, 2, 3, 3, 3 ]</span></span><br></pre></td></tr></table></figure>
<p>바로 <code>flatMap</code>(간혹 <code>concatMap</code>)이라는 개념인데, (a -> ⓑ) 함수를 통해
(ⓐ -> ⓑ)를 만족하는 함수를 말한다. <code>[1, [2], [[3]]]</code>처럼 깊이가 다른 배열을
1차원으로 합치는 함수를 flatten이라고 표현하는데, flatten + map이 아닐까 싶다.
Haskell에서는 Monad라면 Applicative Functor를 만족하고, Applicative Functor라면
Functor를 만족한다. 즉 Monad > Applicative > Functor로 상속하는 구조다. 잠깐
접해본 짧은 생각으로는 독립적인 개념으로 봐도 될 것 같은데(굳이 따지자면
Functor와 Applicative 정도는 has-a로 봐도 될 것 같지만), 내가 놓치고 있는 뭔가가
있는 것 같다.</p>
<hr>
<p>이렇게 대수형 타입끼리 어떻게 연산하는지의 패턴들에 대해 알아봤는데, 왜 이렇게
복잡한 설명과 패턴을 통해 타입을 유지해야 하는가 싶은 생각이 들 수 있다. 가장
기본이 되는 개념은 간단한 개념이니 이것만 알면 된다! 라는 식의 글을 참 많이
봤는데, 내가 원점에 있을 때 (10, 0)쯤에 있던 글보다 쉽다는 글은 (0, 10)쯤에
있었고 그보다 쉽다고 주장하는 글은 (-10, 0)쯤에 있었다. 방향만 달라질 뿐, 거리는
좁혀지지 않는 느낌이었다. 그래도 그나마 알아들을만 했던 예제는 <a target="_blank" rel="noopener" href="http://fsharpforfunandprofit.com/posts/recipe-part2/">Railway oriented
Programming</a>이었다.</p>
<p><img src="/images/wtf-3-fam/1.png" alt=""></p>
<p>Just에 어떤 연산<code>bind</code>을 할 때 결과는 다시 Maybe가 되어야 하니 Just(그림에서의
Success) 혹은 Nothing(그림에서의 Failure) 둘 중 하나가 된다.</p>
<p><img src="/images/wtf-3-fam/2.png" alt=""></p>
<p>그런 연산이 여러 개 존재할 수 있다.</p>
<p><img src="/images/wtf-3-fam/3.png" alt=""></p>
<p>그때, 앞에서 어떤 처리들이 있었고 어디에서 Nothing으로 갔는지 관계없이 현재
들어온 값을 보고 Just인지 Nothing인지 구분(switch)해주는 하나의 블럭을 만들기만
하면 된다.</p>
<p><img src="/images/wtf-3-fam/4.png" alt=""></p>
<p>한번 Nothing이 되면 그 뒤에 어떤 연산이 오든 관계없이 Nothing으로 계속
유지된다. 앞의 어디에서 Nothing이 되었다는 것에 신경 쓰지 않고 현재의 값만 보고
Just인지 Nothing인지 연결하면 된다. 즉, <code>bind</code>(혹은 <code>flatMap</code>)에서는 현재 값과
앞뒤 타입만 맞추면 입력에서 출력까지 연산이 안전하다고 보장된다.</p>
<p>패턴이라는 것은 약속이고, 약속이라는 것은 그것이 보장된다는 말이다. 즉 일종의
추상화로 블랙박스 모델처럼 그림에서의 스위치만 구현해서 레일을 연결하면 안전하게
연산이 잘 흘러간다. OOP처럼 객체 단위의 추상화가 없으니 타입클래스에서 이런
패턴들이 그 역할을 대신하고, 덕분에 재사용하기 좋고 확장 가능해진다. 그런 것들의
기초가 되기 때문에 사람들이 중요하다고 많이 이야기하는 것이라고 생각한다.</p>
<hr>
<p>여전히 이게 뭐다라고 정의내려서 설명하기는 어렵지만 이제 A가 B다라는 말에서 그게
맞거나 틀리다는걸 구분할 수는 있는 것 같다. 사실 이 글을 쓰게 된 목적 중 하나는
이거다. 그동안 함수형 언어를 기껏해야 퀴즈 몇개 풀어보는 정도 이외에는 제대로
써본 적이 없다보니 알듯말듯 한 상태가 몇년째 계속되고 있는데, 최근에 Haskell 책
한권을 읽으면서 그 감이 약간 더 구체화된 김에 정리를 해서 더 잡기 위해서다. 물론
조금 어긋난 내용이 있을 수도 있고 아예 잘못된 내용이 있을 수 있어서 언젠가 이
글을 읽고 이불킥할지도 모르겠지만, 이번 기회에 정리하지 않으면 몇년 더 이해할
기회가 오지 않을 것같다는 느낌이 들었다. 그러니 틀린게 있으면 틀린거고, 아니면
좋고. 이제 Monad라는게 뭔지 대충 정리를 해밨으니 이걸로 뭘 할 수 있는지 한번
써먹어보자.</p>
<p>ps, 타입을 유지하기 위한 연산의 패턴들에 대해서 알아봤는데 이런 것들이 타입론
(Type Theory)이나 범주론(Category Theory)에 속한 것이라면, "정수의 덧셈은 정수에
'닫혀있다'"라고 말하는 것처럼 연산 자체의 성질에 대해서 논하는 군론(Group
Theory)라는 것이 있고 그 중 모노이드(Monoid)라는 개념을 모나드와 함께 사용하면
더 편하게 사용할 수 있는데 그 부분에 대해서는 지금보다 아는 것이 좀 더 생기면
다뤄보고 싶다.</p>
<p>ps2, 다시 말하지만 ps도 맞는지 확신이 없다.</p>
<hr>
<p>Reference</p>
<ul>
<li>
<p>Hackage: Official Haskell Package archive</p>
<ul>
<li><a target="_blank" rel="noopener" href="http://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Monad.html">Control.Monad</a></li>
<li><a target="_blank" rel="noopener" href="http://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Applicative.html">Control.Applicative</a></li>
<li><a target="_blank" rel="noopener" href="http://hackage.haskell.org/package/base-4.8.1.0/docs/src/GHC.Base.html">GHC.Base | Source</a></li>
</ul>
</li>
<li>
<p>Codewars Kata</p>
<ul>
<li><a target="_blank" rel="noopener" href="http://www.codewars.com/kata/monads-the-maybe-monad">Monads: The Maybe Monad</a>
: Maybe = Just | Nothing</li>
</ul>
</li>
<li>
<p><a target="_blank" rel="noopener" href="http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html">Functors, Applicatives, And Monads In Pictures</a> (<a target="_blank" rel="noopener" href="http://netpyoung.github.io/external/functors_applicatives_and_monads_in_pictures/">번역</a>)</p>
</li>
<li>
<p>All Image Credit under <a target="_blank" rel="noopener" href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>
by <a target="_blank" rel="noopener" href="https://twitter.com/ScottWlaschin">ScottWlaschin</a></p>
<ul>
<li>from <a target="_blank" rel="noopener" href="http://fsharpforfunandprofit.com/posts/recipe-part2/">Railway oriented programming</a></li>
</ul>
</li>
</ul>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/functional/" class="post__tag__link">functional</a></li><li class="post__tag__item"><a href="/tags/monad/" class="post__tag__link">monad</a></li><li class="post__tag__item"><a href="/tags/functor/" class="post__tag__link">functor</a></li><li class="post__tag__item"><a href="/tags/applicative/" class="post__tag__link">applicative</a></li></ul><a href="/2015/08/04/wtf-3-fam/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-08-04T13:57:53.000Z" class="post__time">August 4, 2015</time><h1 class="post__title"><a href="/2015/08/04/wtf-2-adt/">WTF - 2. 대수 자료형 (Algebraic Data Type)</a></h1></header><div class="post__main echo"><blockquote>
<p>What is the Functional?</p>
<ol>
<li><a href="/2015/08/04/wtf-1-intro/">Introduction</a></li>
<li><a href="/2015/08/04/wtf-2-adt/">Algebraic Data Type</a></li>
<li><a href="/2015/08/04/wtf-3-fam/">Maybe or Not</a></li>
<li><a href="/2015/08/04/wtf-4-parser/">Monadic Molecule Parser</a></li>
</ol>
</blockquote>
<blockquote>
<p>컴퓨터 프로그래밍에서, 특히 함수형 프로그래밍과 타입 이론에서 대수 자료형은
합성 타입의 한 종류이며 즉, 다른 타입들을 모아서 형성된 타입이다. 제일 보편적인
두 경우로는 프로덕트 타입(product type. 예를 들어 튜플과 레코드)과 섬 타입(sum
type. 혹은 Tagged Union이라 불린다)이 있다.
(중략)
대수 자료형의 값들을 패턴 매칭을 통해 분석해서 생성자나 필드 이름을 통해 값을
알아내거나 내부의 값을 추출해낸다. 대수 자료형은 70년대 에든버러 대학에서 개발한
작은 함수형 언어인 Hope에서 소개됐다.</p>
<p><a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Algebraic_data_type">Algebraic data type - Wikipedia</a></p>
</blockquote>
<p>프로덕트 타입은 여러 타입을 한 번에 쓸 수 있는 구조(튜플의 경우 (A, B), 레코드의
경우 {A: B}같은 형식)이며 섬 타입은 열거형(Union) 비슷한 이름을 갖는 것에서도
추정할 수 있듯 여러 타입 중 하나를 쓸 수 있는 구조다. 여러 언어에서 예전부터
혹은 최근에 도입된 자료형 중에서 Maybe, Option, Optional 등의 이름으로 값을
갖거나(Just) 혹은 값이 없거나(Nothing, None, Null)로 구분되는 자료형이 가장
익숙하지 않을까 싶다. 설명을 하다 보니 OOP에서 추상 클래스와 상속받은 클래스들간
메소드 오버로딩을 통한 다형성과 비슷한 느낌이 든다.</p>
<p>위키피디아의 내용처럼 패턴 매칭에서 주로 사용되는데 패턴 매칭을 지원하지 않는
JavaScript에서는 적용할 수 없는 개념이고 제대로 사용할 수 없으므로 이해하기
어려운 개념이기도 하다. 그런데 굳이 처음부터 잘 이해하고 갈 필요가 있나? 그냥
어떤 것인지 알아보고 JavaScript로 한번 구현해보면 제대로는 아니어도 어떤 것인지
정도는 감을 잡을 수 있을 것이다.</p>
<hr>
<p>대수적 자료형의 재미있는 점은 재귀적 구조와 지연 평가(Lazy Evaluation)을 통해
무한의 자료형이 가능하다는 점인데, Codewars에서 Nat(0 이상의 정수)과 Cons를
JavaScript로 구현하는 문제가 있다.</p>
<h2>Nat</h2>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">data</span> <span class="type">Nat</span> = <span class="type">Zero</span> | <span class="type">Succ</span> <span class="type">Nat</span></span></span><br></pre></td></tr></table></figure>
<p>Haskell에서 대수 자료형 Nat을 정의해보았다. 코드를 정확히 이해할 필요는 없고,
<code>Zero</code>라는 0에 해당하는 값과 Nat을 인자로 받아 그다음 값을 갖는 <code>Succ</code>의 두가지
경우가 될 수 있는 Nat이라는 섬 타입이다. 문제에서는 <code>Zero</code>와 <code>Succ</code>에 대해서
정확히 같지는 않지만, 다음과 비슷하게 주어진다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> zero = <span class="function">() =></span> {};</span><br><span class="line"><span class="keyword">const</span> succ = <span class="function">(<span class="params">nat</span>) =></span> ( <span class="function">()=></span>nat );</span><br></pre></td></tr></table></figure>
<p><code>zero</code>는 일종의 상수고, <code>succ</code>는 nat을 리턴하는 함수를 리턴한다. 즉, 패턴 매칭을
통해 Succ에서 받은 이전 값을 다시 추출하는 과정을 함수 호출로 대신했다. 패턴
매칭을 통해 Nat을 정숫값으로 변환하는 과정도 JavaScript로 대신해보자.</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">toInt</span> :: <span class="type">Nat</span> -> <span class="type">Int</span></span><br><span class="line"><span class="title">toInt</span> <span class="type">Zero</span> = <span class="number">0</span></span><br><span class="line"><span class="title">toInt</span> (<span class="type">Succ</span> nat) = <span class="number">1</span> + toInt nat</span><br></pre></td></tr></table></figure>
<p>인자로 <code>Zero</code>를 받을 경우에는 당연히 0이 되고, <code>Succ</code>를 받았을 경우에 <code>Succ</code>를
생성할 때 받았던 이전 값 <code>nat</code>을 패턴 매칭을 통해 추출해서 다시 <code>toInt</code>로 넘겨서
Nat은 하나씩 이전값을 보고 <code>Zero</code>가 나올 때까지 결괏값을 하나씩 증가시킨다.</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">toInt</span> (<span class="type">Succ</span> (<span class="type">Succ</span> (<span class="type">Succ</span> <span class="type">Zero</span>)))</span><br></pre></td></tr></table></figure>
<p>이렇게 0의 세 번째 다음 값을 정수로 변환한다면 그 과정을 간단히 표현해서
<code>1 + (1 + (1 + 0)</code>이 되고 <code>3</code>을 리턴하게 된다. 그렇다면 이걸 JavaScript로
구현하면</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> toInt = <span class="function">(<span class="params">nat</span>) =></span></span><br><span class="line"> nat === zero</span><br><span class="line"> ? <span class="number">0</span></span><br><span class="line"> : <span class="number">1</span> + toInt(nat())</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(toInt(succ(succ(succ(zero))))); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure>
<p>이런 식으로 재귀를 통해 구현할 수 있다. 혹은 앞에서 언급했던 트램펄린과 비슷하게</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> toInt = <span class="function">(<span class="params">nat</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> count = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(; nat !== zero ; nat = nat(), count++);</span><br><span class="line"> <span class="keyword">return</span> count;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(toInt(succ(succ(succ(zero))))); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure>
<p>구현할 수도 있다. <code>succ</code>와 <code>zero</code>의 의미, 그리고 어떻게 다루어야하는지 방법을
알았으니 나머지 인터페이스는 쉽게 구현할 수 있다.</p>
<h2>Cons</h2>
<p>Codewars에 대수 자료형을 다룬 문제가 하나 더 있다. Nat처럼 재귀적인 구조로 아주
비슷한 Cons라는 구조다. 많은 언어에서 List 혹은 Sequence라는 이름으로 구현체를
제공하는 유명한 자료형이다.</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">data</span> <span class="type">List</span> a = <span class="type">Cons</span> { <span class="title">head</span> :: <span class="title">a</span> , <span class="title">tail</span> :: <span class="type">List</span> <span class="title">a</span>}</span></span><br><span class="line"> | <span class="type">Nil</span></span><br></pre></td></tr></table></figure>
<p>List는 <code>Cons</code> 혹은 <code>Nil</code>이 될 수 있는 섬 타입이다. 그리고 <code>Cons</code>는 다시 <code>a</code>
타입의 <code>head</code>와 <code>List a</code> 타입의 <code>tail</code>로 이루어진다. 위에서 말했듯이 Nat와
유사한 모양의 재귀적인 구조다. 참고로 <code>head</code>와 <code>tail</code>은 Cons 구조에서 일반적으로
쓰이는 이름이지만 언어에 따라서 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/CAR_and_CDR">car과 cdr</a>로
표현하기도 한다. 또한, List와 Array를 모두 제공하는 언어나 라이브러리에서는 보통
C언어에서처럼 고정된 길이의 index를 가지는 배열을 Array(혹은 Vector)라고 부르고
앞서 말했듯 이런 Cons 구조를 List(Sequence)라고 부르는 경우가 많다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Cons</span>(<span class="params">head, tail</span>) </span>{</span><br><span class="line"> <span class="built_in">this</span>.head = head;</span><br><span class="line"> <span class="built_in">this</span>.tail = tail;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> Nil = <span class="keyword">new</span> Cons(<span class="literal">null</span>, <span class="function">()=></span> {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'Empty!'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>문제를 다른 사람이 낸 것인지 앞에서는 함수 호출을 통해 지연 평가를 비슷하게
구현했는데 이번에는 간단하게 속성(property)으로만 구현해서 제공된다. 앞에서의
Nat과 비슷한 정수의 스트림을 구현해보면</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">int</span> :: <span class="type">Int</span> -> <span class="type">List</span> <span class="type">Int</span></span><br><span class="line"><span class="title">int</span> n = <span class="type">Cons</span> n tail</span><br><span class="line"> <span class="keyword">where</span> tail = int (n + <span class="number">1</span>)</span><br></pre></td></tr></table></figure>
<p>이런 식이 된다. <code>tail</code>을 바로 평가하지 않기 때문에 <code>int 0</code>으로 List를 만들어도
바로 <code>Cons 0 tail</code>을 만들 뿐 무한루프를 돌지 않는다. JavaScript에서 비슷하게
만들어보자면</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> int = <span class="function">(<span class="params">n</span>) =></span> <span class="keyword">new</span> Cons(n, <span class="function">()=></span>int(n+<span class="number">1</span>))</span><br><span class="line"></span><br><span class="line">int(<span class="number">0</span>) <span class="comment">// { head: 0, tail: [Function] }</span></span><br><span class="line">int(<span class="number">0</span>).tail() <span class="comment">// { head: 1, tail: [Function] }</span></span><br></pre></td></tr></table></figure>
<p>이렇게 계속 <code>tail</code>을 실행할 때마다 다음 <code>Cons</code>를 생성해서 지연 평가를 구현했다.
사실 링크의 문제에서는 지연 평가에 대한 내용이 없어서 굳이 이렇게까지 할 필요는
없지만, 문제풀이가 목적이 아니니. 그럼 언어가 제공하는 List에서 앞서 정의한
List(Cons|Nil)로 변환을 통해 Cons를 생성할 수 있도록 해보자.</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">fromArray</span> :: [a] -> <span class="type">List</span> a</span><br><span class="line"><span class="title">fromArray</span> [] = <span class="type">Nil</span></span><br><span class="line"><span class="title">fromArray</span> (x:xs) = <span class="type">Cons</span> x (fromArray xs)</span><br></pre></td></tr></table></figure>
<p><code>x:xs</code>는 Cons의 <code>x</code>가 head고 <code>xs</code>가 tail이며 두 개를 연결해서 하나의 Cons를
만든다는 연산자 <code>:</code>이다. 참고로 <code>++</code>의 경우 두 개의 Cons를 연결(concat)한다.
이걸 또 JavaScript로 구현해보면 다음과 같다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fromArray = <span class="function">(<span class="params">arr = []</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span>(arr.length === <span class="number">0</span>) <span class="keyword">return</span> Nil;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">return</span> <span class="keyword">new</span> Cons(arr.shift(), <span class="function">()=></span>fromArray(arr.slice()));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> cons = fromArray([<span class="number">1</span>, <span class="number">2</span>]);</span><br><span class="line"><span class="built_in">console</span>.log(cons); <span class="comment">// { head: 1, tail: [Function] }</span></span><br><span class="line">cons = cons.tail();</span><br><span class="line"><span class="built_in">console</span>.log(cons); <span class="comment">// { head: 2, tail: [Function] }</span></span><br><span class="line">cons = cons.tail();</span><br><span class="line"><span class="built_in">console</span>.log(cons, cons === Nil); <span class="comment">// { head: null, tail: [Function] } true</span></span><br><span class="line">cons = cons.tail();</span><br><span class="line"><span class="comment">// throw new Error('Empty!');</span></span><br><span class="line"><span class="comment">// ^</span></span><br><span class="line"><span class="comment">// Error: Empty!</span></span><br></pre></td></tr></table></figure>
<p>이제 Sequence 타입에 항상 적용해보는 filter와 map을 구현해보자</p>
<figure class="highlight haskell"><figcaption><span>Haskell</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">filter'</span> :: (a -> <span class="type">Bool</span>) -> <span class="type">List</span> a -> <span class="type">List</span> a</span><br><span class="line"><span class="title">filter'</span> f <span class="type">Nil</span> = <span class="type">Nil</span></span><br><span class="line"><span class="title">filter'</span> f (<span class="type">Cons</span> x xs)</span><br><span class="line"> | f x = <span class="type">Cons</span> x (filter' f xs)</span><br><span class="line"> | otherwise = (filter' f xs)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="title">map'</span> :: (a -> b) -> <span class="type">List</span> a -> <span class="type">List</span> b</span><br><span class="line"><span class="title">map'</span> f <span class="type">Nil</span> = <span class="type">Nil</span></span><br><span class="line"><span class="title">map'</span> f (<span class="type">Cons</span> x xs) = <span class="type">Cons</span> (f x) (map' f xs)</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> filter = <span class="function">(<span class="params">f, cons</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span>(cons === Nil) <span class="keyword">return</span> Nil;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span>(!f(cons.head)) <span class="keyword">return</span> filter(f, cons.tail());</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">return</span> <span class="keyword">new</span> Cons(cons.head, <span class="function">()=></span>filter(f, cons.tail()));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> map = <span class="function">(<span class="params">f, cons</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span>(cons === Nil) <span class="keyword">return</span> Nil;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">return</span> <span class="keyword">new</span> Cons(f(cons.head), <span class="function">()=></span>map(f, cons.tail()));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> prt = <span class="function">(<span class="params">cons</span>) =></span> {</span><br><span class="line"> <span class="keyword">while</span>(cons !== Nil) {</span><br><span class="line"> <span class="built_in">console</span>.log(cons.head);</span><br><span class="line"> cons = cons.tail();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>출력이 귀찮아서 <code>prt</code> 함수를 따로 만들었다. 이제 확인을 해보면</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> even = <span class="function">(<span class="params">d</span>) =></span> d % <span class="number">2</span> === <span class="number">0</span>;</span><br><span class="line"><span class="keyword">const</span> doub = <span class="function">(<span class="params">d</span>) =></span> d * <span class="number">2</span>;</span><br><span class="line"><span class="keyword">const</span> toFive = fromArray([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]);</span><br><span class="line"></span><br><span class="line">prt(filter(even, toFive));</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 4</span></span><br><span class="line">prt(map(doub, toFive));</span><br><span class="line"><span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 4</span></span><br><span class="line"><span class="comment">// 6</span></span><br><span class="line"><span class="comment">// 8</span></span><br><span class="line"><span class="comment">// 10</span></span><br></pre></td></tr></table></figure>
<p>이제 재귀적인 구조를 다룰 때 어떻게 실행되는지에 대해 안에서 어떻게 돌아갈지를
간단하게 구현해봤는데 적당히 감이 왔을 것이다. 여기에서 약간의 스킬을 더해서
<code>window.setImmediate</code>나 <code>process.nextTick</code>으로 스레드 점유를 살짝 연기시키면
충분히 큰 자료도 시간만 있으면 다룰 수 있을 것이다. JavaScript에서 <code>map</code>과
<code>filter</code>를 어떻게 쓰는지 모르는 사람은 거의 없을 것이고 Cons에서 구현한 것들도
배열에서 쓰이는 것과 큰 차이가 없었다. 다음에는 이걸 대수적으로 해석할 때의
의미와 그걸 지원하는 구조에 대한 명칭을 이야기할 것이다.</p>
<hr>
<p>Reference</p>
<ul>
<li>Codewars Kata
<ul>
<li><a target="_blank" rel="noopener" href="http://www.codewars.com/kata/algebraic-data-types">Algebraic Data Types</a>
: Nat = Zero | Succ Nat</li>
<li><a target="_blank" rel="noopener" href="http://www.codewars.com/kata/algebraic-lists">Algebraic List</a>
: List a = Cons {a, List a} | Nil</li>
</ul>
</li>
<li>함수프로그래밍 실천기술(가칭), 제이펍 2015</li>
</ul>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/functional/" class="post__tag__link">functional</a></li><li class="post__tag__item"><a href="/tags/algebraic-data-type/" class="post__tag__link">algebraic data type</a></li><li class="post__tag__item"><a href="/tags/maybe/" class="post__tag__link">maybe</a></li></ul><a href="/2015/08/04/wtf-2-adt/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-08-04T13:57:16.000Z" class="post__time">August 4, 2015</time><h1 class="post__title"><a href="/2015/08/04/wtf-1-intro/">WTF - 1. 시작</a></h1></header><div class="post__main echo"><blockquote>
<p>What is the Functional?</p>
<ol>
<li><a href="/2015/08/04/wtf-1-intro/">Introduction</a></li>
<li><a href="/2015/08/04/wtf-2-adt/">Algebraic Data Type</a></li>
<li><a href="/2015/08/04/wtf-3-fam/">Maybe or Not</a></li>
<li><a href="/2015/08/04/wtf-4-parser/">Monadic Molecule Parser</a></li>
</ol>
</blockquote>
<p>얼마 전에 지인과 맥주 한잔을 하다가 iOS 개발을 Swift로 하고 있다 보니 함수형으로
못 쓰는 것도 아닌데 같이 일하는 사람들이 배울 생각이 없어서 도입하기 어렵다는
말을 들었다. 요즘 여기저기에서 폴리글랏이니 병렬처리니 하는 말과 함께 함수형
패러다임이 무엇인지 어디에 좋다는지 하는 설명은 많이 들려오고 있어 관심이
있는 사람은 많지만, 그래서 그걸 구체적으로 어디에 어떻게 쓰이는지에 대해 잘
몰라 동기부여가 되지 않는 사람들이 많다. 나도 입문 단계를 아직 벗어나지
못했다고 생각해서 앞으로 쓸 글에 잘못된 점이 있을 수도 있지만Codewars의 문제
몇 개를 풀어보면서 이게 어떤 모습인지, 어떻게 쓰이는지 한번 써볼까 한다(그러니
이상하거나 틀린 점을 발견한다면 <a target="_blank" rel="noopener" href="https://github.com/seoh/blog/issues">이슈</a>에
남겨주길 바란다) . 사실 개발자가 뭔가를 배우는 데는 생업에 관련된 게
큰 이유겠지만 가장 좋은 동기부여는 역시 재미가 아닐까 싶다. 이걸 보고 뭔가
재미있게 쓰인다고 느끼고 서점가서 책이라도 한번 펼쳐보는 사람이 생긴다면 이
글의 목표는 성공한 것이다.</p>
<p>시작하기에 앞서서 앞으로 관심을 가질 사람들에게 가장 도움이 될만한 것은 내가
어떤 책을 읽고 어떻게 공부해왔는지부터 말하는 것이 아닐까. 예전의
<a href="/2015/01/18/pis-review/">PiS 리뷰</a>에서 썼듯이
처음에 재미를 느끼고 뭔가 해보기 시작한 것은 <a target="_blank" rel="noopener" href="http://www.hanbit.co.kr/ebook/look.html?isbn=9788979149678">자바 개발자를 위한 함수형
프로그래밍</a>과
<a target="_blank" rel="noopener" href="http://hanbit.co.kr/book/look.html?isbn=978-89-6848-079-9">함수형 자바스크립트</a>
에서다. 함수형 언어라고 불리는 것들을 배우면 함수형 사고에 큰 도움이 되지만
처음부터 너무 큰 벽을 넘으려고 하면 지치기 쉽다. 그런 의미에서 지금 가장 익숙한
언어가 적당히 함수형을 쓸 수 있도록 지원한다면 그걸로 먼저 조금씩 사고를 익히는
것이 좋다. JavaScript도 좋고 Java 8도 충분하고 Swift라면 훌륭하다.</p>
<hr>
<p>이미 많은 사람이 알고 있지만 그래도 가장 먼저 익숙해지면 좋은 개념은 함수를
자료형으로 생각하는 것이다. First-class function이니 Higher-order function이라는
개념들이 있지만 결국 함수도 하나의 자료형이라는 개념에 조건을 붙인 것이다.
함수(혹은 메소드)에서 정적인 자료를 넘기는 것에 익숙한 사람들이 가장 이질적으로
느끼는 것이 이런 것으로 생각한다. 함수를 넘기고 함수를 만들어서 리턴하고,
함수끼리 연산해서 새로운 함수를 만들고. 함수형 자바스크립트라는 책에서 독자에게
훈련시키는 이런 것이 아닐까 싶다. 그럼 익숙한 것부터 시작해보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> count = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">try</span>{ (<span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>)</span>{ count++; <span class="keyword">return</span> a(); })(); }</span><br><span class="line"><span class="keyword">catch</span>(e) { <span class="built_in">console</span>.log(e.message, count); }</span><br><span class="line"><span class="comment">// Maximum call stack size exceeded 17926</span></span><br></pre></td></tr></table></figure>
<p>JavaScript로 코딩하다 보면 재귀함수에서 로직을 잘못 짜거나 생각했던 범위를 훨씬
넘는 값을 받았을 때 저런 문제가 발생한다. 대부분은 이미 아는 내용이겠지만 잠시
다른 이야기를 해보자면, 기계는 코드를 한 줄씩 읽어서 해석하다가 어딘가로
넘어가게 될 때(CALL), 현재 가지고 있는 값들을 어딘가에 저장해두고 다시 돌아올
위치를 기록한 다음에 넘어가게 된다. 그래서 어딘가로 계속 넘어가다 보면 기계의
물리적 혹은 논리적인 한계 때문에 실행환경에서 익셉션을 발생시키거나 프로세스가
강제로 종료된다. 많은 언어에서는 꼬리 재귀 최적화(Tail Recursion Optimization)
혹은 꼬리 호출 제거(Tail-call Elimination)라고 불리는 기능을 지원하는데,
어딘가(A)로 한번 넘어갔을 때 거기에서 다시 어딘가(B)로 넘어갈 때 A에서의 상태를
굳이 저장하지 않아도 된다면 돌아올 곳은 똑같으니 현재 값들을 저장하고 돌아올
곳을 기록하는 작업을 생략한 채 그냥 B로 넘어가는 식(JMP)으로 최적화가 가능하다.</p>
<p>그렇다면 JavaScript에서는 언어적인 한계 때문에 충분한 시간이 있어도 충분히 많은
재귀를 할 수 없을까? JavaScript에서도 어떤 패턴을 통해 콜스택을 일정하게 유지할
수 있다(하지만 아마 다소 메모리의 증가는 생길 것이다). 간단한 예를 들어서 다음과
같은 피보나치 수열이 있다고 해보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fibo = <span class="function">(<span class="params">n</span>) =></span></span><br><span class="line"> n < <span class="number">3</span></span><br><span class="line"> ? <span class="number">1</span></span><br><span class="line"> : fibo(n-<span class="number">1</span>) + fibo(n-<span class="number">2</span>);</span><br></pre></td></tr></table></figure>
<p>위에서 말했듯이 저장할 상태가 없어야하는데 양쪽으로 재귀를 돌다 보니 하나를 돌때
다른 하나를 저장하고 있어야 한다. 그러니 구조를 약간 바꾸어보자.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> f = <span class="function">(<span class="params">a, b, n</span>) =></span></span><br><span class="line"> n < <span class="number">2</span></span><br><span class="line"> ? a</span><br><span class="line"> : f(b, a+b, n-<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fibo2 = <span class="function">(<span class="params">n</span>) =></span> f(<span class="number">1</span>, <span class="number">1</span>, n);</span><br></pre></td></tr></table></figure>
<p>인자가 많이 늘어났지만 저장해야하는 상태없이 다음 재귀로 넘어갈 수 있게 되었다.
이제 콜 스택의 깊이를 유지하려면 함수를 재귀적으로 실행시키지 않고 실행하고
끝난 뒤에 다시 실행하도록 순서를 변경하면 된다.</p>
<figure class="highlight js"><figcaption><span>JavaScript</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> f2 = <span class="function">(<span class="params">a, b, n</span>) =></span></span><br><span class="line"> n < <span class="number">2</span></span><br><span class="line"> ? a</span><br><span class="line"> : <span class="function">() =></span> f2(b, a+b, n-<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fibo3 = <span class="function">(<span class="params">n</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> t = <span class="function">() =></span> f2(<span class="number">1</span>, <span class="number">1</span>, n);</span><br><span class="line"> <span class="keyword">while</span>(<span class="keyword">typeof</span> (t = t()) === <span class="string">'function'</span>);</span><br><span class="line"> <span class="keyword">return</span> t;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>다음 실행될 재귀를 함수로 감싸서 리턴하면 한번에 하나씩 실행이 되며, 함수가 아닌
값을 받았을 때 재귀를 대신한 루프가 종료된다. 이해하기 편하도록 원형<code>fibo2</code>을
최대한 유지해서 <code>fibo3</code>를 작성했고, 이걸 일반화시킨 패턴을 트램펄린(trampoline)
이라고 부른다. 사실 트램펄린 자체는 함수형 패턴의 예시라기보다 CPS(Continuation
passing style)의 예라고 설명하는 쪽이 더 적절하다. 다만 이 패턴이 재미있는 것은
JavaScript에서 지원하지 않는 꼬리 재귀 최적화와 지연평가의 개념, 그리고 우회
구현이 동시에 들어있기 때문에 언어의 한계를 기술적으로 우회한다는 점에서, 그리고
그 기반에 대한 개념이 함수형과 맞닿아있다는 점에서 예시로 가져왔다. 함수형
자바스크립트 책을 처음 읽었을 때 이해하기 어려워서 미뤄뒀는데 계속 함수형처럼
코딩하려고 노력하다 나중에 다시 읽으니 그 전에 왜 이해하지 못했나 싶었다. 아마
위에서 언급했던 것처럼 자료형처럼 넘기고 받는다는 생각에 이질감이 들어서였을까.</p>
<hr>
<p>Reference</p>
<ul>
<li><a target="_blank" rel="noopener" href="http://www.eriwen.com/javascript/cps-tail-call-elimination/">Continuation-passing and tail call elimination in Javascript</a></li>
<li><a target="_blank" rel="noopener" href="http://dogfeet.github.io/articles/2012/by-example-continuation-passing-style-in-javascript.html">예제로 설명하는 자바스크립트에서의 Continuation-passing style</a></li>
<li>함수형 자바스크립트, 한빛미디어 2014</li>
</ul>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/functional/" class="post__tag__link">functional</a></li><li class="post__tag__item"><a href="/tags/trampoline/" class="post__tag__link">trampoline</a></li></ul><a href="/2015/08/04/wtf-1-intro/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-06-30T07:05:11.000Z" class="post__time">June 30, 2015</time><h1 class="post__title"><a href="/2015/06/30/sociality-of-translation/">번역어의 사회성</a></h1></header><div class="post__main echo"><p><a target="_blank" rel="noopener" href="http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=898890222X">책상은 책상이다</a>라는
책이 있다. 중고등학교 때 언어의 많은 특징에 대해 배우는데, 그중에서 언어의
사회성이라는 것을 배운다. 언어란 사람들 간의 의사소통을 위한 도구이며, 그래서
추상적인 생각이든 구체적인 물건이든 어떤 의미에 대해서 어떻게 부르자는 사람들 간의
약속을 언어의 사회성이라고 한다. 저 책에서는 그 사회성을 무시했을 때 발생할 수
있는 문제들에 대한 이야기를 다루고 있다.</p>
<hr>
<p><a target="_blank" rel="noopener" href="https://slack.com/">슬랙</a>이라는 메시징 앱이 있는데 이 앱이 유행하면서 아직
채팅 공간이 없는 커뮤니티들은 IRC 대신 슬랙을 사용하고 있고, 그래서 이것저것
추가를 하다 보니 10개를 넘어갔다가 다시 추려서 7개만 남았는데, 그중에서 가장
오랜 시간을 보고 있고, 계속 1번에 위치했던 <a target="_blank" rel="noopener" href="http://weirdx.io/">이상한 모임</a> 팀에
질문이 올라왔다. (슬랙은 업무용 툴이라 하나의 단위가 팀이다. 사실 이 글이 중복
게시되는 메타블로그를 통해 보는 사람이 대다수라 부연설명이 필요 없을 수도 있지만,
혹시 모르는 분들이 있을까봐 남겨봤다. )</p>
<blockquote>
<p>"Lazy evaluation를 뭐라고 번역해야 좋을까요?"</p>
</blockquote>
<p>한국어 위키에 "<a target="_blank" rel="noopener" href="https://ko.wikipedia.org/wiki/%EB%8A%90%EA%B8%8B%ED%95%9C_%EA%B3%84%EC%82%B0%EB%B2%95">느긋한 계산법</a>"이라고
되어있다며 질문이 올라왔고 질문한 분은 "이따 평가"라는 제안을 했고 나는
"지연평가가 가장 흔한 번역같습니다."라고 대답했다.</p>
<p>필요해질 때까지 평가를 미뤄둔다는 의미에서 Lazy의 번역으로 '지연'이라는 표현을
많이 사용하는데, 지연이라는 말에 타의적 혹은 일정 시간이라는 정적인 뉘앙스가
있다고 생각해서 원래 의미가 살지 못해서 좋은 번역은 아니라고 생각한다. 다만,
그 번역을 들었을만한 사람(특히 개발자)들은 대부분 "Lazy Evaluation"이라는 원문과
동시에 그 원문의 뜻을 같이 떠올릴 수 있을 것이다. 좋은 번역은 아니지만, 대부분은
뉘앙스를 알 수 있다는 의미로 나는 "합의된 번역"이라고 표현한다.</p>
<hr>
<p>도입에서 말한 언어의 사회성이라는 것은 번역어에도 적용된다. 하지만 언어끼리의
1:1 매칭이 되는 표현은 거의 존재하지 않는다는 특수성 때문에 번역어에서의 사회성은
언어의 사회성과 다소 차이가 있다. 자연스럽게 시간이 흐르면서 생기는 사회적 합의
없이, 이미 존재하는 언어의 이미 존재하는 표현을 갑자기 다른 언어의 다른 표현으로
옮기는 것에는 사회성이 존재하기 어렵다. 그래서 많은 번역어가 사회적 합의 없이
생겨나고 생긴 뒤에서야 사람들이 그 표현에 익숙해지는 후불제 합의에 가깝다.
그래서 어떤 분야를 맨 처음 번역하는 사람들의 역할이 중요하다. 처음 시도했던
번역어가 후세에도 많은 영향을 끼치기 마련이고, 그 영향을 벗어나 새로운 합의를
만들어내기까지 오랜 시간과 노력이 필요하기 때문이다. 그 시간과 노력을 투자하는
사람은 언어 자체를 공부하는 사람이 될 수도 있고, 또 다른 번역자일 수도 있다.
하지만 누군가의 주도 하에 결정되기보다 사회적 합의를 이루기 위해 커뮤니티의
주도로 이루어졌으면 좋겠다는 생각을 한다. 페이스북의 <a target="_blank" rel="noopener" href="https://www.facebook.com/groups/engfordev/">개발자영어</a>라는
그룹에서 종종 그런 글들이 올라온다. 이런 말은 보통 이렇게 번역하는데 더 좋은
표현이 없을까 하는 질문글. 저 그룹이 어떤 권위나 영향력이 있지는 않지만, 합의를
위해 토론이 이루어지는 (내가 아는 한에서)유일한 커뮤니티이다.</p>
<p>이 생각을 맨 처음 했던 것은 얼마 전에 읽었던 책 때문이다. 유명한 역자분이
번역했고, 역자 서문에서부터 현재의 (합의된)번역어보다 더 어울리는 말을 찾기 위해
많이 고민하고 노력했다고 적혀있고 책을 읽으면서 생소한 단어 때문에 읽는 데 방해도
많이 되긴 했지만, 무슨 뜻일까 거꾸로 영어로 영작해보기도 하고 원문을 찾아보고
한자어의 경우 한자 뜻을 찾아보고 나서야 어떤 의도로 그런 번역어를 만들었다고
이해하기도 했고 혹은 아무 문제 없다고 생각했던 번역어를 굳이 쓰지 않고 곡해하기
좋은 새로운 번역어라고 생각되는 경우도 있었다.</p>
<p>책을 읽으며 아쉬웠던 점은 합의없이 새로운 표현을 만들어냈다는 것이 아니라, 생소한
표현을 처음 사용할 때는 차라리 원어 병기를 통해 유추라도 가능하도록 도와줬으면
좋았을 텐데 그렇지 못한 경우가 너무 많았다는 점 정도다. 번역어에 대한 합의는
현재 그럴만한 적당한 공간이 없기 때문에 그걸 생략했다고 누구에게 뭐라고 할 상황은
아니라고 생각한다. 그래서 더더욱 그럴만한 적당한 공간이 없다는데 더 안타깝다.</p>
<hr>
<p>마무리로 광고를 남기자면 이상한 모임의 <a target="_blank" rel="noopener" href="http://weirdmeetup.herokuapp.com">슬랙</a>에
들어와 보시면 이 공간의 아이덴티티를 이해하는 데 도움이 된다. 위에서 말한 번역에
대한 이야기도 간혹 하고, 개발자가 많다보니 개발에 관한 채널이 많지만 그 이외에도
각종 가젯(gadget)/SW 지름, 음악, 책, 디자인, 운동 등 아주 잡다한 분야에 대한
이야기가 항상 오고가서 어떤 사람들이 어떤 이야기를 한다고 특별히 정의내려서
설명하기 어렵다. 그냥 직접 와서 겪어보는게 이해하는데 제일 좋다. 굳이 공통점을
꼽자면 재미있는 것에 목마른 사람들이라고 표현하고 싶다.</p>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/translation/" class="post__tag__link">translation</a></li></ul><a href="/2015/06/30/sociality-of-translation/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-06-28T17:46:17.000Z" class="post__time">June 29, 2015</time><h1 class="post__title"><a href="/2015/06/29/big-data-with-spark-5-week/">Spark로 빅데이터 입문, 5주차 및 후기</a></h1></header><div class="post__main echo"><h1>5주차. 스파크로 머신러닝 시작</h1>
<p>이번 주의 제목은 노트가 아니라 메모 겸 후기다.
5주차에는 수업이 없고 과제와 퀴즈만 있다.</p>
<h3>Lab 4. 스파크로 머신러닝 시작</h3>
<p>영화 목록과 평점 이력을 트레이닝 셋으로 해서 내가 영화 평점을 몇 개 입력해서
다른 영화의 내 평점을 예측하도록 기계학습을 해보는 과제이다.
스파크의 머신러닝 라이브러리(MLlib)에서의 협업 필터링(Collaborative Filtering)
에서는 ALS(Alternating Least Squares)라는 알고리즘을 사용하는데, 유사도를
평가하는 데는 <a target="_blank" rel="noopener" href="https://ko.wikipedia.org/wiki/%ED%8F%89%EA%B7%A0_%EC%A0%9C%EA%B3%B1%EA%B7%BC_%ED%8E%B8%EC%B0%A8">평균 제곱근 오차(Root Mean Square Error; RMSE)</a>
라는 방법을 사용한다. 정확한 의미는 이해하지 못했지만, 순서대로 따라가니 풀 수
있었다.</p>
<h3>Lab 4. Quiz</h3>
<p>RMSE의 값에 대한 의미(예상값과 실제값이 같을 때의 결괏값)를 묻는 간단한
문제들이었다.</p>
<h1>후기</h1>
<p><img src="/images/big-data-with-spark-5-week/score.png" alt=""></p>
<p>세 번째 과제를 진행하다가 TF-IDF에 대한 이해가 부족해서 자료를 찾다가 영어로 된
글을 계속 읽다 보니 지루해져서 계속 미뤘는데, 결국 기한을 넘겨서 그냥 하던 데
까지만 제출했다. 그래서 이번 과제는 알고리즘(ALS)에 대한 이해가 부족해도 그냥
최대한 설명을 자세히 읽고 이리저리 시도해보다가 다 풀긴 했다.</p>
<p>내용을 다 이해하지는 못했지만, 좋은 입문 강의다. 강의 시작에서 언급했듯이 파이썬
기본 문법 정도만 알고 있으면 진행하는 데 큰 무리는 없을 것이라 생각한다. 어차피
기초적인 개념부터 설명하는 강의라, 과제할 때 파이썬 문법의 문제인지 스파크를
잘못 사용한 것인지에 대해 구분할 수 있을 정도면 되지만, 그렇지 않을 때는
어렵다기보다 상당히 까다로울 것이다.</p>
<p>기초적인 개념부터 설명한다고 위에서 말했지만, 개념과 역사, 사례를 넓게 훑고
지나가면서 책, 논문 등의 자료들을 레퍼런스로 많이 소개해서 깊게 알고 싶은 분야에
대한 좋은 진입점을 제시해준다. 당연히 입문 강의는 그렇다고 생각하지만 A to Z로
가르쳐주길 원하는 사람에게는 맞지 않는 강의다.</p>
<p>가장 마음에 들었던 것들을 꼽자면, 하나는 모든 강의가 5분 내로 되어있다는 점이고
나머지 하나는 <a href="/2012/11/17/after-coursera-fppis-and-test">FPPiS</a>처럼 과제가
단계별 테스트로 되어있다는 점이다.</p>
<p>입문 강의라 많은 개념을 깊게 설명할 수 없으므로 개념별로 간단하게 설명을
하기 지나가는데, 덕분에 지하철/버스에서 이동 중에 틈틈이 듣고 나중에 1.5배속으로
빠르게 복기하면서 퀴즈를 풀면 두세 번 반복하는 느낌이라 오래(과제가 끝나기 전까지)
기억에 남는다. 이동하는 시간은 어차피 낭비하는 시간이라고 생각했는데 꽤 요긴하게
쓰였다.</p>
<p>그리고 단계별 테스트로 되어있다는 것도 입문 과목에서 큰 장점이라고 생각하는데,
과제를 던져주고 알아서 해결하는 방법을 찾는 것도 중요하지만 가장 정석적인
단계를 알려주기 위해서는 과제를 단계별로 나누고 각각의 단계를 어떻게 진행할지에
대한 설명을 사이사이에 주고, 하나가 통과해야 그다음으로 넘어갈 수 있으니 다음
문제를 보고 이전 문제의 의도를 가늠해볼 수 있기도 하다. 물론 하나가 막혀버리면
그다음의 모든 것을 못한다는 게 단점이지만 그렇게 난이도 조절을 못 한 과제는
아니라고 생각한다.</p>
<hr>
<p>사실 다 마치고 나서도 이제 무엇을 해야할지 막막하지만, 소재의 문제이지 방법에
대한 것은 한번 과정을 거쳤으니 어떤 식으로 접근해야 할지에 대한 감을 대충 알았다.</p>
<p>참고로 edX에서는 한 학교가 주제에 따라 코스웍을 제공하는 X시리즈 인증이 있는데,
이 강의(CS100.1x)는 Berkeley에서 진행하는 빅데이터 코스인 BerkeleyX의 두 단계 중
첫 번째다. 두 번째 단계(CS109.1x)는 <a target="_blank" rel="noopener" href="https://www.edx.org/course/scalable-machine-learning-uc-berkeleyx-cs190-1x">확장 가능한 머신 러닝(Scalable Machine Learning)</a>
이라는 제목으로 29일부터 시작한다. 수학적 사고와 알고리즘 개념, 그리고 기본적인
머신 러닝에 대한 개념이 필요하며, 알고리즘, 확률, 선형대수, 미적분을 접해본 적이
있고, 파이썬 경험이 있거나 빠르게 익힐 수 있으면 된다. 듣긴 하겠지만, 이 과목처럼
완주하겠다는 생각으로 듣는 것은 아니고 재미있는 부분까지만.</p>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/spark/" class="post__tag__link">spark</a></li><li class="post__tag__item"><a href="/tags/bigdata/" class="post__tag__link">bigdata</a></li></ul><a href="/2015/06/29/big-data-with-spark-5-week/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article><article class="post"><header class="post__head"><time datetime="2015-06-25T18:24:00.000Z" class="post__time">June 26, 2015</time><h1 class="post__title"><a href="/2015/06/26/big-data-with-spark-4-week/">Spark로 빅데이터 입문, 4주차 노트</a></h1></header><div class="post__main echo"><h1>4주차. 데이터 품질, 탐헌적 데이터 분석과 머신 러닝</h1>
<h3>Lecture 7. 데이터 품질</h3>
<p>데이터 클리닝</p>
<ul>
<li>왜곡: 처리과정에서 변질된 표본들</li>
<li>선택편견: 값에 따른 표본의 가능도(likelihood)</li>
<li>좌우검열: 데이터가 무한대일 때 시작과 끝을 어떻게 자를지</li>
<li>의존성: 표본이 독립적인지 아닌지에 대한 판단</li>
</ul>
<ul>
<li>정확성과 (과정의)간소에 대한 트레이드오프</li>
<li>단위 통일, 중복 제거 등</li>
</ul>
<p>문제</p>
<ul>
<li>텍스트 파싱</li>
<li>같은 엔티티 다른 표현(2 vs two, NYC vs NewYork)</li>
<li>비구조적-구조적 전환시 primary key</li>
<li>너무 길어서 잘리는 필드</li>
<li>형식 문제(특히 날짜)</li>
</ul>
<p>수집</p>
<ul>
<li>과정에서 무결성 체크</li>
<li>구조에 없는건 기본값</li>
</ul>
<p>전송</p>
<ul>
<li>신뢰할만한 프로토콜인가</li>
<li>받은 데이터의 확인이 가능한가(checksum)</li>
</ul>
<p>분석의 어려움</p>
<ul>
<li>크기, 성능</li>
<li>모델에 적용</li>
<li>전문지식 부족</li>
<li>다트판(때려맞추기)</li>
<li>대충 경험(특정 상황에만 맞는 분석)</li>
</ul>
<p>품질 측정</p>
<ul>
<li>스키마 일치</li>
<li>정확성, 접근성, 해석가능</li>
<li>Lab2에서 정규식을 통한 형식 일치 확인</li>
</ul>
<p>용어?</p>
<ul>
<li>개체 식별(entity resolution)</li>
<li>중복 검출(DeDup: Detection Duplicated)</li>
</ul>
<p>표준화</p>
<ul>
<li>USPS에서 제공하는 <a target="_blank" rel="noopener" href="http://pe.usps.com/text/pub28/welcome.htm">주소 표준가이드</a></li>
<li>다른 필드 참고 등 식별 힌트</li>
</ul>
<h3>Lecture 8. 탐험적 데이터 분석과 머신 러닝</h3>
<p>기술통계 vs 추론통계(<a target="_blank" rel="noopener" href="https://ko.wikipedia.org/wiki/%ED%86%B5%EA%B3%84%ED%95%99#.EC.B6.94.EB.A1.A0_.ED.86.B5.EA.B3.84">위키피디아</a>)</p>
<p>업무에서의 목적</p>
<ul>
<li>간단한 통계</li>
<li>가설 검증</li>
<li>분류</li>
<li>예측</li>
</ul>
<p><a target="_blank" rel="noopener" href="http://www.amazon.com/dp/0201076160">탐험적 데이터분석</a></p>
<ul>
<li>기본 테크닉 소개
<ul>
<li><a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Five-number_summary">Five-number summary</a></li>
<li>box plot, stem and leaf diagram</li>
</ul>
</li>
<li>통계요약의 문제: 같은 요약이라도 다른 데이터일 수 있다</li>
</ul>
<p>정규 분포</p>
<ul>
<li>평균, 표준편차</li>
<li>중심극한정리(Central Limit Theorem): n이 무한대로 가면 정규분포에 가까워진다.</li>
</ul>
<p>다른 중요한 분포</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://ko.wikipedia.org/wiki/%ED%91%B8%EC%95%84%EC%86%A1_%EB%B6%84%ED%8F%AC">프아송 분포</a></li>
<li>이항 분포, 다항 분포</li>
</ul>
<p>Spark의 mllib</p>
<ul>
<li>NumPy와 함께 사용가능(pySpark >= 0.9)</li>
<li>여기에서는 영화평점 예측
<ul>
<li>collaborative filtering</li>
<li>k rank = user(a) x movie feature(b)</li>
</ul>
</li>
</ul>
<h3>Lab 3. 텍스트 분석과 개체 식별</h3>
<ol>
<li>텍스트 유사성으로 개체 식별 - Bags of Words</li>
</ol>
<ul>
<li><a target="_blank" rel="noopener" href="http://darkpgmr.tistory.com/125">Bag of Words 기법</a></li>
</ul>
<ol start="2">
<li>텍스트 유사성으로 개체 식별- TF-IDF를 사용한 가중치 적용된 BOW</li>
</ol>
<ul>
<li><a target="_blank" rel="noopener" href="https://ko.wikipedia.org/wiki/TF-IDF">TF-IDF</a></li>
</ul>
<ol start="3">
<li>텍스트 유사성으로 개체 식별- 코사인 유사도(Cosine Similarity)</li>
<li>역참조(inverted index)를 통한 효율적인 개체 식별</li>
<li>그래프(plot)을 통한 결과 분석</li>
</ol>
<h3>Lab 3. 퀴즈</h3>
<p>Lab 3에서 배운 것들 재확인</p>
<hr>
<p><a target="_blank" rel="noopener" href="https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/pyspark">pySpark Docset</a></p>
<ul>
<li><a target="_blank" rel="noopener" href="https://kapeli.com/dash">Dash</a>용 pySpark API문서</li>
<li>설정의 다운로드 -> 좌하단의 사용자 제공(User Contibuted) -> pySpark 검색</li>
</ul>
<hr>
<ul>
<li><a target="_blank" rel="noopener" href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week4Lec7.pdf">Lecture 7 slides</a></li>
<li><a target="_blank" rel="noopener" href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week4Lec8.pdf">Lecture 8 slides</a></li>
</ul>
</div><footer class="post__foot u-cf"><ul class="post__tag u-fl"><li class="post__tag__item"><a href="/tags/spark/" class="post__tag__link">spark</a></li><li class="post__tag__item"><a href="/tags/bigdata/" class="post__tag__link">bigdata</a></li></ul><a href="/2015/06/26/big-data-with-spark-4-week/#disqus_thread" class="post__foot-link u-fr">0 COMMENTS</a></footer></article></main><footer class="foot"><div class="foot-copy u-fl">© 2021 Seoh Char</div><menu class="page-menu u-fr"><li class="page-menu__item"><span title="Previous" class="page-menu__link icon-arrow-left page-menu__link--disabled"></span></li><li class="page-menu__item"><a title="Next" href="/page/2/" class="page-menu__link icon-arrow-right"></a></li></menu></footer></body></html>