<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
  xmlns:atom="http://www.w3.org/2005/Atom"
  xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>devthewild</title>
    <link>http://seoh.blog/</link>
    
    <image>
      <url>https://www.gravatar.com/avatar/fd930454c8c06c30c7fddc27b4bee475</url>
      <title>devthewild</title>
      <link>http://seoh.blog/</link>
    </image>
    
    <atom:link href="http://seoh.blog/rss.xml" rel="self" type="application/rss+xml"/>
    
    <description>most opinionated blog</description>
    <pubDate>Tue, 01 Jun 2021 13:48:15 GMT</pubDate>
    <generator>http://hexo.io/</generator>
    
    <item>
      <title>Coursera - EPiS 후기</title>
      <link>http://seoh.blog/2021/06/01/after-coursera-epis/</link>
      <guid>http://seoh.blog/2021/06/01/after-coursera-epis/</guid>
      <pubDate>Tue, 01 Jun 2021 13:44:34 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;Scala 3(a.k.a Dotty)의 업데이트와 함께 새로운 스칼라 입문 코스, &lt;a href=&quot;https://www.coursera.org/learn/effective-scala&quot;&gt;Effective Programming in Scala&lt;/a&gt;</description>
        
      
      
      
      <content:encoded><![CDATA[<p>Scala 3(a.k.a Dotty)의 업데이트와 함께 새로운 스칼라 입문 코스, <a href="https://www.coursera.org/learn/effective-scala">Effective Programming in Scala</a>가 코세라에 올라왔다. <a 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>&#123;&#125;</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 &lt;- 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 =&gt; 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.&#123;Pkg =&gt; P&#125;</code>였다면 이제는 <code>as</code>라는 키워드를 사용해 <code>import from.to.&#123;Pkg as P&#125;</code>처럼 쓰면 된다.</p><p>새로운 <code>given</code> 키워드와 새로운 문법상 맥락이 생기며 <code>given</code> 변수들은 <code>.*</code>로 가져올 수 없으니 given을 한번에 가져오려면 <code>import from.to.given</code>을 쓰거나 given을 포함한 다른 멤버들도 한번에 가져오려면 <code>import from.to&#123;given, *&#125;</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.&#123;given Ordering[Int]&#125;</code></li><li>타입*: <code>import Ordering.&#123;given Ordering[?]&#125;</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>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/coursera/">coursera</category>
      
      <category domain="http://seoh.blog/tags/Scala/">Scala</category>
      
      
      <comments>http://seoh.blog/2021/06/01/after-coursera-epis/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>2015 정리</title>
      <link>http://seoh.blog/2016/01/02/2015-summary/</link>
      <guid>http://seoh.blog/2016/01/02/2015-summary/</guid>
      <pubDate>Sat, 02 Jan 2016 05:22:28 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;남들 다 하니까 해볼 겸, 한번 배워보고 싶지만 매번 미루던 Google Analytics를 다른 방법으로 써볼 겸
이것저것 눌러보며 다른 블로그에서 제공되는 요약 내용을 시도해봤다.&lt;/p&gt;
&lt;h3&gt;1. PV&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/i</description>
        
      
      
      
      <content:encoded><![CDATA[<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 href="https://courses.edx.org/courses/BerkeleyX/CS100.1x/1T2015/">Introduction to Big Data with Apache Spark</a>를 들으면서 정리한 노트인데, 내 트위터에 바로 공유했던 1-2주차는 전혀 관심을 받지 못했고, 3주차를 쓰고 하루 뒤에 관련 그룹(페이스북의 <a 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>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/annual/">annual</category>
      
      
      <comments>http://seoh.blog/2016/01/02/2015-summary/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Elm Reousrces</title>
      <link>http://seoh.blog/2015/08/10/elm-resources/</link>
      <guid>http://seoh.blog/2015/08/10/elm-resources/</guid>
      <pubDate>Mon, 10 Aug 2015 09:34:02 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;얼마전에 Haskell 책을 읽었더니, 예전에 관심은 있었지만 어디서부터 접근해야할지
몰라서 미뤄뒀던 &lt;a href=&quot;http://elm-lang.org/&quot;&gt;Elm&lt;/a&gt;이 생각났다. 어떤 것이라고 정리할 정도로 아직 잘 알지
못해서 또 미뤄두고</description>
        
      
      
      
      <content:encoded><![CDATA[<p>얼마전에 Haskell 책을 읽었더니, 예전에 관심은 있었지만 어디서부터 접근해야할지몰라서 미뤄뒀던 <a href="http://elm-lang.org/">Elm</a>이 생각났다. 어떤 것이라고 정리할 정도로 아직 잘 알지못해서 또 미뤄두고 있었는데, 혹시나 관심을 가지고 있는 분이 있다면 함께공부할 수 있었으면 좋겠다는 생각에 어떤 점에서 관심을 가지게 되었고 어떤 것을봐왔는지 정리해두려고 한다.</p><h2>time travel debugger</h2><p>요새 react-hot-loader를 통해 hot swap(코드 수정 후 현재 상태까지 다시 컨트롤하지않고 그 상태로 바로 반영하는 것)이 가능하다고 사람들이 환호했는데, 그 이전에Clojurescript의 Leiningen plugin으로 <a 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 href="http://debug.elm-lang.org/">time travel debugger</a>가 이미 있었다.자세한 내용은 당시 <a 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 href="http://elm-lang.org/guide/reactivity">Signal</a>이라는 흐름 위에서 돌아가도록설계되어있어서 그런 것이라 추정한다. (아직 뜯어보지 않았다.)</p><h2>blazing fast</h2><p>vDOM/react의 구현체 속도 벤치마크가 유행했던 적이 있는데 거기에서 뜬금없이등장한 적이 있다. 홈페이지에도 <a href="http://elm-lang.org/blog/blazing-fast-html">자랑</a>이 올라와있는데 벤치마크 결과는다음과 같다. 참고로 <a 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 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 href="https://pragmaticstudio.com/elm">Elm: Building Reactive Web Apps</a>라는유료 강의가 올라왔는데, 들어보지않아서 어떤 강의인지는 잘 모르겠다.</p><h2>other drugs</h2><ul><li><a href="https://www.classes.cs.uchicago.edu/archive/2015/winter/22300-1/">Functional Programming</a><ul><li>시카고대에서 올해 초 겨울학기 강의를 Elm으로 진행했다. 조만간 읽을 예정.</li><li>0.14 기준이라 <a href="http://elm-lang.org/blog/announce/0.15#introducing-mailboxes">Mailbox와 Task</a> 대신 Channel이 있어서 지금 그대로따라하기는 어렵다.</li></ul></li><li><a 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 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 href="https://gist.github.com/mgold/461dbf37d4d34767e5da">Using Mailboxes in Elm</a><ul><li>Actor Model처럼 Mailbox라는 개념을 통해서 Message를 보내는데 문서가 부족해서누가 대신 쓴 글</li></ul></li><li><a 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 href="https://pragprog.com/book/7lang/seven-more-languages-in-seven-weeks">Seven More Languages in Seven Weeks</a><ul><li>&quot;You’ll learn seven exciting languages: Lua, Factor, Elixir, Elm, Julia, MiniKanren, and Idris.&quot;</li><li>유일하게 Elm을 언급한 책인데 전작인 <a 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>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/Elm/">Elm</category>
      
      
      <comments>http://seoh.blog/2015/08/10/elm-resources/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>WTF - 4. 모나드 분자식 파서(Monadic Molecule Parser)</title>
      <link>http://seoh.blog/2015/08/04/wtf-4-parser/</link>
      <guid>http://seoh.blog/2015/08/04/wtf-4-parser/</guid>
      <pubDate>Tue, 04 Aug 2015 13:58:10 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;What is the Functional?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-1-intro/&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-2</description>
        
      
      
      
      <content:encoded><![CDATA[<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>) =&gt;</span> str.match(regToken);</span><br><span class="line"><span class="keyword">const</span> lexer = <span class="function">(<span class="params">arr</span>) =&gt;</span> arr.reduce(<span class="function">(<span class="params">r,t</span>) =&gt;</span> ..., [])</span><br><span class="line"><span class="keyword">const</span> parse = <span class="function">(<span class="params">form</span>) =&gt;</span> &#123;</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>&#123;...&#125;</span><br><span class="line">&#125;</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 href="https://en.wikipedia.org/wiki/Monad_(functional_programming)#State_monads">State</a>라는패턴이 있다. 모양은 대충 <code>type State s a = s -&gt; (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 -&gt; (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 =&gt; Parser</span></span><br><span class="line"><span class="keyword">const</span> ahead   = <span class="function"><span class="params">p</span> =&gt;</span> <span class="function">(<span class="params">str</span>) =&gt;</span> p(str) ? [<span class="string">&#x27;&#x27;</span>, str] : [];</span><br><span class="line"><span class="comment">///// Parser =&gt; Parser</span></span><br><span class="line"><span class="keyword">const</span> satisfy = <span class="function"><span class="params">p</span> =&gt;</span> <span class="function">(<span class="params">str</span>) =&gt;</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> =&gt;</span> <span class="string">&#x27;A&#x27;</span> &lt;= ch[<span class="number">0</span>] &amp;&amp; ch[<span class="number">0</span>] &lt;= <span class="string">&#x27;Z&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log( upper(<span class="string">&quot;H&quot;</span>) ); <span class="comment">// [ &#x27;H&#x27;, &#x27;&#x27; ]</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) -&gt; Parser</span></span><br><span class="line"><span class="keyword">const</span> and = <span class="function">(<span class="params">pa, pb</span>) =&gt;</span> <span class="function">(<span class="params">str</span>) =&gt;</span> &#123;</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">&#125;;</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> =&gt;</span> <span class="string">&#x27;a&#x27;</span> &lt;= ch[<span class="number">0</span>] &amp;&amp; ch[<span class="number">0</span>] &lt;= <span class="string">&#x27;z&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(and(upper, lower)(<span class="string">&quot;Mg&quot;</span>)); <span class="comment">// [ &#x27;Mg&#x27;, &#x27;&#x27; ]</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> =&gt;</span> str.length &gt; <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> =&gt;</span> <span class="string">&#x27;A&#x27;</span> &lt;= ch[<span class="number">0</span>] &amp;&amp; ch[<span class="number">0</span>] &lt;= <span class="string">&#x27;Z&#x27;</span>));</span><br><span class="line"><span class="keyword">const</span> lower = and(chr, satisfy(<span class="function"><span class="params">ch</span> =&gt;</span> <span class="string">&#x27;a&#x27;</span> &lt;= ch[<span class="number">0</span>] &amp;&amp; ch[<span class="number">0</span>] &lt;= <span class="string">&#x27;z&#x27;</span>));</span><br><span class="line"><span class="keyword">const</span> digit = and(chr, satisfy(<span class="function"><span class="params">ch</span> =&gt;</span> <span class="string">&#x27;0&#x27;</span> &lt;= ch[<span class="number">0</span>] &amp;&amp; ch[<span class="number">0</span>] &lt;= <span class="string">&#x27;9&#x27;</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) -&gt; Parser</span></span><br><span class="line"><span class="keyword">const</span> or = <span class="function">(<span class="params">pa, pb</span>) =&gt;</span> <span class="function">(<span class="params">str</span>) =&gt;</span> &#123;</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 &gt; <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">&#125;;</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">&quot;Mg&quot;</span>)); <span class="comment">// [ &#x27;Mg&#x27;, &#x27;&#x27; ]</span></span><br><span class="line"><span class="built_in">console</span>.log(atom(<span class="string">&quot;H&quot;</span>));  <span class="comment">// [ &#x27;H&#x27;, &#x27;&#x27; ]</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>) =&gt;</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>) =&gt;</span> <span class="function">(<span class="params">str</span>) =&gt;</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>) =&gt;</span> <span class="function">(<span class="params">str</span>) =&gt;</span> or(p, <span class="function">(<span class="params">str</span>) =&gt;</span> [<span class="string">&#x27;&#x27;</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">&quot;H2O&quot;</span>)); <span class="comment">// H2O</span></span><br></pre></td></tr></table></figure><p>이제 <code>satisfy(ch=&gt;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 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 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 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 href="https://en.wikibooks.org/wiki/Haskell/ParseExps">Haskell/ParseExps</a></li></ul></li><li><p>FP101x</p><ul><li><a href="https://www.edx.org/course/introduction-functional-programming-delftx-fp101x-0">edX course 10/15~</a></li><li><a href="https://github.com/fptudelft/FP101x-Content">Contents</a></li></ul></li></ul>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/functional/">functional</category>
      
      <category domain="http://seoh.blog/tags/monad/">monad</category>
      
      
      <comments>http://seoh.blog/2015/08/04/wtf-4-parser/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>WTF - 3. Maybe or Not</title>
      <link>http://seoh.blog/2015/08/04/wtf-3-fam/</link>
      <guid>http://seoh.blog/2015/08/04/wtf-3-fam/</guid>
      <pubDate>Tue, 04 Aug 2015 13:58:04 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;What is the Functional?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-1-intro/&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-2</description>
        
      
      
      
      <content:encoded><![CDATA[<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>&#123;</span><br><span class="line">  <span class="function"><span class="title">toString</span>(<span class="params"></span>)</span> &#123; <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&quot;Must be implemented.&quot;</span>); &#125;</span><br><span class="line">&#125;</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>&#123;</span><br><span class="line">  <span class="function"><span class="title">constructor</span>(<span class="params">v</span>)</span> &#123;</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">  &#125;</span><br><span class="line">  <span class="function"><span class="title">toString</span>(<span class="params"></span>)</span> &#123; <span class="keyword">return</span> <span class="string">`Just <span class="subst">$&#123;<span class="built_in">this</span>.value.toString()&#125;</span>`</span>; &#125;</span><br><span class="line">&#125;</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>&#123;</span><br><span class="line">  <span class="function"><span class="title">constructor</span>(<span class="params">v</span>)</span> &#123;</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">  &#125;</span><br><span class="line">  <span class="function"><span class="title">toString</span>(<span class="params"></span>)</span> &#123; <span class="keyword">return</span> <span class="string">&quot;Nothing&quot;</span>; &#125;</span><br><span class="line">&#125;</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 -&gt; Maybe a</span></span><br><span class="line"><span class="keyword">const</span> unit = <span class="function">(<span class="params">x</span>) =&gt;</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>) =&gt;</span></span><br><span class="line">  m.constructor.name === <span class="string">&quot;Nothing&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">///// fmap : Maybe a, (a -&gt; b) -&gt; Maybe b</span></span><br><span class="line"><span class="keyword">const</span> fmap = <span class="function">(<span class="params">m, fn</span>) =&gt;</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>) =&gt;</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 -&gt; b), Maybe a -&gt; Maybe b</span></span><br><span class="line"><span class="keyword">const</span> appl = <span class="function">(<span class="params">mfn, ma</span>) =&gt;</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>) =&gt;</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 -&gt; Maybe b) -&gt; Maybe b</span></span><br><span class="line"><span class="keyword">const</span> bind = <span class="function">(<span class="params">ma, fn</span>) =&gt;</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>) =&gt;</span> unit(doub(d));             <span class="comment">// a -&gt; 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 -&gt; b) -&gt; 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 -&gt; b) -&gt; Array b</span></span><br><span class="line"><span class="keyword">const</span> amap = <span class="function">(<span class="params">arr, fn</span>) =&gt;</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> =&gt;</span> <span class="built_in">String</span>(n))) );  <span class="comment">// [ &#x27;1&#x27;, &#x27;2&#x27;, &#x27;3&#x27; ]</span></span><br></pre></td></tr></table></figure><p>a라는 타입의 값을 가지고 있는 어떤 타입을 ⓐ라고 하고, b의 경우를 ⓑ라고 하면,(a -&gt; b) 함수를 통해 결과적으로 (ⓐ -&gt; ⓑ)를 만족하도록 연산할 수 있는 타입을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 -&gt; b), Maybe a -&gt; 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> =&gt;</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> =&gt;</span> d + <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">///// Array (a -&gt; b), Array a -&gt; 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>) =&gt;</span> fns.map(<span class="function"><span class="params">fn</span> =&gt;</span> <span class="keyword">as</span>.map(fn))</span><br><span class="line">                             .reduce(<span class="function">(<span class="params">r,b</span>) =&gt;</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 -&gt; b)를 가지고 있는 Array와 Array a를 통해 Array b를 만들었다. 위에서말했듯 함수를 가지고 있는 Functor(Array (a -&gt; 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 -&gt; Maybe b) -&gt; Maybe b</span></span><br></pre></td></tr></table></figure><p>JavaScript에서는 <a 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>&#123;</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>) =&gt;</span> r.concat(a), [])</span><br><span class="line">&#125;;</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> =&gt;</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 -&gt; ⓑ) 함수를 통해(ⓐ -&gt; ⓑ)를 만족하는 함수를 말한다. <code>[1, [2], [[3]]]</code>처럼 깊이가 다른 배열을1차원으로 합치는 함수를 flatten이라고 표현하는데, flatten + map이 아닐까 싶다.Haskell에서는 Monad라면 Applicative Functor를 만족하고, Applicative Functor라면Functor를 만족한다. 즉 Monad &gt; Applicative &gt; Functor로 상속하는 구조다. 잠깐접해본 짧은 생각으로는 독립적인 개념으로 봐도 될 것 같은데(굳이 따지자면Functor와 Applicative 정도는 has-a로 봐도 될 것 같지만), 내가 놓치고 있는 뭔가가있는 것 같다.</p><hr><p>이렇게 대수형 타입끼리 어떻게 연산하는지의 패턴들에 대해 알아봤는데, 왜 이렇게복잡한 설명과 패턴을 통해 타입을 유지해야 하는가 싶은 생각이 들 수 있다. 가장기본이 되는 개념은 간단한 개념이니 이것만 알면 된다! 라는 식의 글을 참 많이봤는데, 내가 원점에 있을 때 (10, 0)쯤에 있던 글보다 쉽다는 글은 (0, 10)쯤에있었고 그보다 쉽다고 주장하는 글은 (-10, 0)쯤에 있었다. 방향만 달라질 뿐, 거리는좁혀지지 않는 느낌이었다. 그래도 그나마 알아들을만 했던 예제는 <a href="http://fsharpforfunandprofit.com/posts/recipe-part2/">Railway orientedProgramming</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)에 속한 것이라면, &quot;정수의 덧셈은 정수에'닫혀있다'&quot;라고 말하는 것처럼 연산 자체의 성질에 대해서 논하는 군론(GroupTheory)라는 것이 있고 그 중 모노이드(Monoid)라는 개념을 모나드와 함께 사용하면더 편하게 사용할 수 있는데 그 부분에 대해서는 지금보다 아는 것이 좀 더 생기면다뤄보고 싶다.</p><p>ps2, 다시 말하지만 ps도 맞는지 확신이 없다.</p><hr><p>Reference</p><ul><li><p>Hackage: Official Haskell Package archive</p><ul><li><a href="http://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Monad.html">Control.Monad</a></li><li><a href="http://hackage.haskell.org/package/base-4.8.1.0/docs/Control-Applicative.html">Control.Applicative</a></li><li><a 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 href="http://www.codewars.com/kata/monads-the-maybe-monad">Monads: The Maybe Monad</a>: Maybe = Just | Nothing</li></ul></li><li><p><a href="http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html">Functors, Applicatives, And Monads In Pictures</a> (<a href="http://netpyoung.github.io/external/functors_applicatives_and_monads_in_pictures/">번역</a>)</p></li><li><p>All Image Credit under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>by <a href="https://twitter.com/ScottWlaschin">ScottWlaschin</a></p><ul><li>from <a href="http://fsharpforfunandprofit.com/posts/recipe-part2/">Railway oriented programming</a></li></ul></li></ul>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/functional/">functional</category>
      
      <category domain="http://seoh.blog/tags/monad/">monad</category>
      
      <category domain="http://seoh.blog/tags/functor/">functor</category>
      
      <category domain="http://seoh.blog/tags/applicative/">applicative</category>
      
      
      <comments>http://seoh.blog/2015/08/04/wtf-3-fam/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>WTF - 2. 대수 자료형 (Algebraic Data Type)</title>
      <link>http://seoh.blog/2015/08/04/wtf-2-adt/</link>
      <guid>http://seoh.blog/2015/08/04/wtf-2-adt/</guid>
      <pubDate>Tue, 04 Aug 2015 13:57:53 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;What is the Functional?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-1-intro/&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-2</description>
        
      
      
      
      <content:encoded><![CDATA[<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. 예를 들어 튜플과 레코드)과 섬 타입(sumtype. 혹은 Tagged Union이라 불린다)이 있다.(중략)대수 자료형의 값들을 패턴 매칭을 통해 분석해서 생성자나 필드 이름을 통해 값을알아내거나 내부의 값을 추출해낸다. 대수 자료형은 70년대 에든버러 대학에서 개발한작은 함수형 언어인 Hope에서 소개됐다.</p><p><a 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">() =&gt;</span> &#123;&#125;;</span><br><span class="line"><span class="keyword">const</span> succ = <span class="function">(<span class="params">nat</span>) =&gt;</span> ( <span class="function">()=&gt;</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> -&gt; <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>) =&gt;</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>) =&gt;</span> &#123;</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">&#125;</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> &#123; <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>&#125;</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 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>&#123;</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">&#125;</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">()=&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&#x27;Empty!&#x27;</span>);</span><br><span class="line">&#125;);</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> -&gt; <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>) =&gt;</span> <span class="keyword">new</span> Cons(n, <span class="function">()=&gt;</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">// &#123; head: 0, tail: [Function] &#125;</span></span><br><span class="line">int(<span class="number">0</span>).tail() <span class="comment">// &#123; head: 1, tail: [Function] &#125;</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] -&gt; <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>) =&gt;</span> &#123;</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">()=&gt;</span>fromArray(arr.slice()));</span><br><span class="line">&#125;</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">// &#123; head: 1, tail: [Function] &#125;</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">// &#123; head: 2, tail: [Function] &#125;</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">// &#123; head: null, tail: [Function] &#125; true</span></span><br><span class="line">cons = cons.tail();</span><br><span class="line"><span class="comment">//   throw new Error(&#x27;Empty!&#x27;);</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&#x27;</span> :: (a -&gt; <span class="type">Bool</span>) -&gt; <span class="type">List</span> a -&gt; <span class="type">List</span> a</span><br><span class="line"><span class="title">filter&#x27;</span> f <span class="type">Nil</span> = <span class="type">Nil</span></span><br><span class="line"><span class="title">filter&#x27;</span> f (<span class="type">Cons</span> x xs)</span><br><span class="line">  | f x       = <span class="type">Cons</span> x (filter&#x27; f xs)</span><br><span class="line">  | otherwise = (filter&#x27; f xs)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="title">map&#x27;</span> :: (a -&gt; b) -&gt; <span class="type">List</span> a -&gt; <span class="type">List</span> b</span><br><span class="line"><span class="title">map&#x27;</span> f <span class="type">Nil</span> = <span class="type">Nil</span></span><br><span class="line"><span class="title">map&#x27;</span> f (<span class="type">Cons</span> x xs) = <span class="type">Cons</span> (f x) (map&#x27; 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>) =&gt;</span> &#123;</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">()=&gt;</span>filter(f, cons.tail()));</span><br><span class="line">&#125;</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>) =&gt;</span> &#123;</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">()=&gt;</span>map(f, cons.tail()));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> prt = <span class="function">(<span class="params">cons</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">while</span>(cons !== Nil) &#123;</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">  &#125;</span><br><span class="line">&#125;</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>) =&gt;</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>) =&gt;</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 href="http://www.codewars.com/kata/algebraic-data-types">Algebraic Data Types</a>: Nat = Zero | Succ Nat</li><li><a 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>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/functional/">functional</category>
      
      <category domain="http://seoh.blog/tags/algebraic-data-type/">algebraic data type</category>
      
      <category domain="http://seoh.blog/tags/maybe/">maybe</category>
      
      
      <comments>http://seoh.blog/2015/08/04/wtf-2-adt/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>WTF - 1. 시작</title>
      <link>http://seoh.blog/2015/08/04/wtf-1-intro/</link>
      <guid>http://seoh.blog/2015/08/04/wtf-1-intro/</guid>
      <pubDate>Tue, 04 Aug 2015 13:57:16 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;What is the Functional?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-1-intro/&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2015/08/04/wtf-2</description>
        
      
      
      
      <content:encoded><![CDATA[<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 href="https://github.com/seoh/blog/issues">이슈</a>에남겨주길 바란다) . 사실 개발자가 뭔가를 배우는 데는 생업에 관련된 게큰 이유겠지만 가장 좋은 동기부여는 역시 재미가 아닐까 싶다.  이걸 보고 뭔가재미있게 쓰인다고 느끼고 서점가서 책이라도 한번 펼쳐보는 사람이 생긴다면 이글의 목표는 성공한 것이다.</p><p>시작하기에 앞서서 앞으로 관심을 가질 사람들에게 가장 도움이 될만한 것은 내가어떤 책을 읽고 어떻게 공부해왔는지부터 말하는 것이 아닐까. 예전의<a href="/2015/01/18/pis-review/">PiS 리뷰</a>에서 썼듯이처음에 재미를 느끼고 뭔가 해보기 시작한 것은 <a href="http://www.hanbit.co.kr/ebook/look.html?isbn=9788979149678">자바 개발자를 위한 함수형프로그래밍</a>과<a 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>&#123; (<span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params"></span>)</span>&#123; count++; <span class="keyword">return</span> a(); &#125;)(); &#125;</span><br><span class="line"><span class="keyword">catch</span>(e) &#123; <span class="built_in">console</span>.log(e.message, count); &#125;</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>) =&gt;</span></span><br><span class="line">  n &lt; <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>) =&gt;</span></span><br><span class="line">  n &lt; <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>) =&gt;</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>) =&gt;</span></span><br><span class="line">  n &lt; <span class="number">2</span></span><br><span class="line">    ? a</span><br><span class="line">    : <span class="function">() =&gt;</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>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> t = <span class="function">() =&gt;</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">&#x27;function&#x27;</span>);</span><br><span class="line">  <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>다음 실행될 재귀를 함수로 감싸서 리턴하면 한번에 하나씩 실행이 되며, 함수가 아닌값을 받았을 때 재귀를 대신한 루프가 종료된다. 이해하기 편하도록 원형<code>fibo2</code>을최대한 유지해서 <code>fibo3</code>를 작성했고, 이걸 일반화시킨 패턴을 트램펄린(trampoline)이라고 부른다. 사실 트램펄린 자체는 함수형 패턴의 예시라기보다 CPS(Continuationpassing style)의 예라고 설명하는 쪽이 더 적절하다. 다만 이 패턴이 재미있는 것은JavaScript에서 지원하지 않는 꼬리 재귀 최적화와 지연평가의 개념, 그리고 우회구현이 동시에 들어있기 때문에 언어의 한계를 기술적으로 우회한다는 점에서, 그리고그 기반에 대한 개념이 함수형과 맞닿아있다는 점에서 예시로 가져왔다.  함수형자바스크립트 책을 처음 읽었을 때 이해하기 어려워서 미뤄뒀는데 계속 함수형처럼코딩하려고 노력하다 나중에 다시 읽으니 그 전에 왜 이해하지 못했나 싶었다. 아마위에서 언급했던 것처럼 자료형처럼 넘기고 받는다는 생각에 이질감이 들어서였을까.</p><hr><p>Reference</p><ul><li><a href="http://www.eriwen.com/javascript/cps-tail-call-elimination/">Continuation-passing and tail call elimination in Javascript</a></li><li><a href="http://dogfeet.github.io/articles/2012/by-example-continuation-passing-style-in-javascript.html">예제로 설명하는 자바스크립트에서의 Continuation-passing style</a></li><li>함수형 자바스크립트, 한빛미디어 2014</li></ul>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/functional/">functional</category>
      
      <category domain="http://seoh.blog/tags/trampoline/">trampoline</category>
      
      
      <comments>http://seoh.blog/2015/08/04/wtf-1-intro/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>번역어의 사회성</title>
      <link>http://seoh.blog/2015/06/30/sociality-of-translation/</link>
      <guid>http://seoh.blog/2015/06/30/sociality-of-translation/</guid>
      <pubDate>Tue, 30 Jun 2015 07:05:11 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;&lt;a href=&quot;http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=898890222X&quot;&gt;책상은 책상이다&lt;/a&gt;라는
책이 있다. 중고등학교 때 언어의 많은 특징에 대해 배우는데, 그중에서 언어의
사회성이라는 것을</description>
        
      
      
      
      <content:encoded><![CDATA[<p><a href="http://www.aladin.co.kr/shop/wproduct.aspx?ISBN=898890222X">책상은 책상이다</a>라는책이 있다. 중고등학교 때 언어의 많은 특징에 대해 배우는데, 그중에서 언어의사회성이라는 것을 배운다. 언어란 사람들 간의 의사소통을 위한 도구이며, 그래서추상적인 생각이든 구체적인 물건이든 어떤 의미에 대해서 어떻게 부르자는 사람들 간의약속을 언어의 사회성이라고 한다. 저 책에서는 그 사회성을 무시했을 때 발생할 수있는 문제들에 대한 이야기를 다루고 있다.</p><hr><p><a href="https://slack.com/">슬랙</a>이라는 메시징 앱이 있는데 이 앱이 유행하면서 아직채팅 공간이 없는 커뮤니티들은 IRC 대신 슬랙을 사용하고 있고, 그래서 이것저것추가를 하다 보니 10개를 넘어갔다가 다시 추려서 7개만 남았는데, 그중에서 가장오랜 시간을 보고 있고, 계속 1번에 위치했던 <a href="http://weirdx.io/">이상한 모임</a> 팀에질문이 올라왔다.  (슬랙은 업무용 툴이라 하나의 단위가 팀이다. 사실 이 글이 중복게시되는 메타블로그를 통해 보는 사람이 대다수라 부연설명이 필요 없을 수도 있지만,혹시 모르는 분들이 있을까봐 남겨봤다. )</p><blockquote><p>&quot;Lazy evaluation를 뭐라고 번역해야 좋을까요?&quot;</p></blockquote><p>한국어 위키에 &quot;<a 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>&quot;이라고되어있다며 질문이 올라왔고 질문한 분은 &quot;이따 평가&quot;라는 제안을 했고 나는&quot;지연평가가 가장 흔한 번역같습니다.&quot;라고 대답했다.</p><p>필요해질 때까지 평가를 미뤄둔다는 의미에서 Lazy의 번역으로 '지연'이라는 표현을많이 사용하는데, 지연이라는 말에 타의적 혹은 일정 시간이라는 정적인 뉘앙스가있다고 생각해서 원래 의미가 살지 못해서 좋은 번역은 아니라고 생각한다. 다만,그 번역을 들었을만한 사람(특히 개발자)들은 대부분 &quot;Lazy Evaluation&quot;이라는 원문과동시에 그 원문의 뜻을 같이 떠올릴 수 있을 것이다. 좋은 번역은 아니지만, 대부분은뉘앙스를 알 수 있다는 의미로 나는 &quot;합의된 번역&quot;이라고 표현한다.</p><hr><p>도입에서 말한 언어의 사회성이라는 것은 번역어에도 적용된다. 하지만 언어끼리의1:1 매칭이 되는 표현은 거의 존재하지 않는다는 특수성 때문에 번역어에서의 사회성은언어의 사회성과 다소 차이가 있다. 자연스럽게 시간이 흐르면서 생기는 사회적 합의없이, 이미 존재하는 언어의 이미 존재하는 표현을 갑자기 다른 언어의 다른 표현으로옮기는 것에는 사회성이 존재하기 어렵다. 그래서 많은 번역어가 사회적 합의 없이생겨나고 생긴 뒤에서야 사람들이 그 표현에 익숙해지는 후불제 합의에 가깝다.그래서 어떤 분야를 맨 처음 번역하는 사람들의 역할이 중요하다. 처음 시도했던번역어가 후세에도 많은 영향을 끼치기 마련이고, 그 영향을 벗어나 새로운 합의를만들어내기까지 오랜 시간과 노력이 필요하기 때문이다. 그 시간과 노력을 투자하는사람은 언어 자체를 공부하는 사람이 될 수도 있고, 또 다른 번역자일 수도 있다.하지만 누군가의 주도 하에 결정되기보다 사회적 합의를 이루기 위해 커뮤니티의주도로 이루어졌으면 좋겠다는 생각을 한다. 페이스북의 <a href="https://www.facebook.com/groups/engfordev/">개발자영어</a>라는그룹에서 종종 그런 글들이 올라온다. 이런 말은 보통 이렇게 번역하는데 더 좋은표현이 없을까 하는 질문글. 저 그룹이 어떤 권위나 영향력이 있지는 않지만, 합의를위해 토론이 이루어지는 (내가 아는 한에서)유일한 커뮤니티이다.</p><p>이 생각을 맨 처음 했던 것은 얼마 전에 읽었던 책 때문이다. 유명한 역자분이번역했고, 역자 서문에서부터 현재의 (합의된)번역어보다 더 어울리는 말을 찾기 위해많이 고민하고 노력했다고 적혀있고 책을 읽으면서 생소한 단어 때문에 읽는 데 방해도많이 되긴 했지만, 무슨 뜻일까 거꾸로 영어로 영작해보기도 하고 원문을 찾아보고한자어의 경우 한자 뜻을 찾아보고 나서야 어떤 의도로 그런 번역어를 만들었다고이해하기도 했고 혹은 아무 문제 없다고 생각했던 번역어를 굳이 쓰지 않고 곡해하기좋은 새로운 번역어라고 생각되는 경우도 있었다.</p><p>책을 읽으며 아쉬웠던 점은 합의없이 새로운 표현을 만들어냈다는 것이 아니라, 생소한표현을 처음 사용할 때는 차라리 원어 병기를 통해 유추라도 가능하도록 도와줬으면좋았을 텐데 그렇지 못한 경우가 너무 많았다는 점 정도다. 번역어에 대한 합의는현재 그럴만한 적당한 공간이 없기 때문에 그걸 생략했다고 누구에게 뭐라고 할 상황은아니라고 생각한다. 그래서 더더욱 그럴만한 적당한 공간이 없다는데 더 안타깝다.</p><hr><p>마무리로 광고를 남기자면 이상한 모임의 <a href="http://weirdmeetup.herokuapp.com">슬랙</a>에들어와 보시면 이 공간의 아이덴티티를 이해하는 데 도움이 된다. 위에서 말한 번역에대한 이야기도 간혹 하고, 개발자가 많다보니 개발에 관한 채널이 많지만 그 이외에도각종 가젯(gadget)/SW 지름, 음악, 책, 디자인, 운동 등 아주 잡다한 분야에 대한이야기가 항상 오고가서 어떤 사람들이 어떤 이야기를 한다고 특별히 정의내려서설명하기 어렵다. 그냥 직접 와서 겪어보는게 이해하는데 제일 좋다. 굳이 공통점을꼽자면 재미있는 것에 목마른 사람들이라고 표현하고 싶다.</p>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/translation/">translation</category>
      
      
      <comments>http://seoh.blog/2015/06/30/sociality-of-translation/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Spark로 빅데이터 입문, 5주차 및 후기</title>
      <link>http://seoh.blog/2015/06/29/big-data-with-spark-5-week/</link>
      <guid>http://seoh.blog/2015/06/29/big-data-with-spark-5-week/</guid>
      <pubDate>Sun, 28 Jun 2015 17:46:17 GMT</pubDate>
      
        
        
      <description>&lt;h1&gt;5주차. 스파크로 머신러닝 시작&lt;/h1&gt;
&lt;p&gt;이번 주의 제목은 노트가 아니라 메모 겸 후기다.
5주차에는 수업이 없고 과제와 퀴즈만 있다.&lt;/p&gt;
&lt;h3&gt;Lab 4. 스파크로 머신러닝 시작&lt;/h3&gt;
&lt;p&gt;영화 목록과 평점 이력을 트레이닝 셋으</description>
        
      
      
      
      <content:encoded><![CDATA[<h1>5주차. 스파크로 머신러닝 시작</h1><p>이번 주의 제목은 노트가 아니라 메모 겸 후기다.5주차에는 수업이 없고 과제와 퀴즈만 있다.</p><h3>Lab 4. 스파크로 머신러닝 시작</h3><p>영화 목록과 평점 이력을 트레이닝 셋으로 해서 내가 영화 평점을 몇 개 입력해서다른 영화의 내 평점을 예측하도록 기계학습을 해보는 과제이다.스파크의 머신러닝 라이브러리(MLlib)에서의 협업 필터링(Collaborative Filtering)에서는 ALS(Alternating Least Squares)라는 알고리즘을 사용하는데, 유사도를평가하는 데는 <a 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 href="https://www.edx.org/course/scalable-machine-learning-uc-berkeleyx-cs190-1x">확장 가능한 머신 러닝(Scalable Machine Learning)</a>이라는 제목으로 29일부터 시작한다. 수학적 사고와 알고리즘 개념, 그리고 기본적인머신 러닝에 대한 개념이 필요하며, 알고리즘, 확률, 선형대수, 미적분을 접해본 적이있고, 파이썬 경험이 있거나 빠르게 익힐 수 있으면 된다. 듣긴 하겠지만, 이 과목처럼완주하겠다는 생각으로 듣는 것은 아니고 재미있는 부분까지만.</p>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/spark/">spark</category>
      
      <category domain="http://seoh.blog/tags/bigdata/">bigdata</category>
      
      
      <comments>http://seoh.blog/2015/06/29/big-data-with-spark-5-week/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Spark로 빅데이터 입문, 4주차 노트</title>
      <link>http://seoh.blog/2015/06/26/big-data-with-spark-4-week/</link>
      <guid>http://seoh.blog/2015/06/26/big-data-with-spark-4-week/</guid>
      <pubDate>Thu, 25 Jun 2015 18:24:00 GMT</pubDate>
      
        
        
      <description>&lt;h1&gt;4주차. 데이터 품질, 탐헌적 데이터 분석과 머신 러닝&lt;/h1&gt;
&lt;h3&gt;Lecture 7. 데이터 품질&lt;/h3&gt;
&lt;p&gt;데이터 클리닝&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;왜곡: 처리과정에서 변질된 표본들&lt;/li&gt;
&lt;li&gt;선택편견: 값에 따른 표본의 가능도(l</description>
        
      
      
      
      <content:encoded><![CDATA[<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 href="http://pe.usps.com/text/pub28/welcome.htm">주소 표준가이드</a></li><li>다른 필드 참고 등 식별 힌트</li></ul><h3>Lecture 8. 탐험적 데이터 분석과 머신 러닝</h3><p>기술통계 vs 추론통계(<a 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 href="http://www.amazon.com/dp/0201076160">탐험적 데이터분석</a></p><ul><li>기본 테크닉 소개<ul><li><a 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 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 &gt;= 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 href="http://darkpgmr.tistory.com/125">Bag of Words 기법</a></li></ul><ol start="2"><li>텍스트 유사성으로 개체 식별- TF-IDF를 사용한 가중치 적용된 BOW</li></ol><ul><li><a 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 href="https://github.com/Kapeli/Dash-User-Contributions/tree/master/docsets/pyspark">pySpark Docset</a></p><ul><li><a href="https://kapeli.com/dash">Dash</a>용 pySpark API문서</li><li>설정의 다운로드 -&gt; 좌하단의 사용자 제공(User Contibuted) -&gt; pySpark 검색</li></ul><hr><ul><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week4Lec7.pdf">Lecture 7 slides</a></li><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week4Lec8.pdf">Lecture 8 slides</a></li></ul>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/spark/">spark</category>
      
      <category domain="http://seoh.blog/tags/bigdata/">bigdata</category>
      
      
      <comments>http://seoh.blog/2015/06/26/big-data-with-spark-4-week/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Spark로 빅데이터 입문, 3주차 노트</title>
      <link>http://seoh.blog/2015/06/14/big-data-with-spark-3-week/</link>
      <guid>http://seoh.blog/2015/06/14/big-data-with-spark-3-week/</guid>
      <pubDate>Sun, 14 Jun 2015 14:43:28 GMT</pubDate>
      
        
        
      <description>&lt;h3&gt;Lecture 5. 반구조적 데이터&lt;/h3&gt;
&lt;p&gt;자료 형태&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;구조적: 정형(schema) 데이터. RDB, formatted msg&lt;/li&gt;
&lt;li&gt;반구조적: schema를 그때그때. XML, JSON, mp3tag&lt;/l</description>
        
      
      
      
      <content:encoded><![CDATA[<h3>Lecture 5. 반구조적 데이터</h3><p>자료 형태</p><ul><li>구조적: 정형(schema) 데이터. RDB, formatted msg</li><li>반구조적: schema를 그때그때. XML, JSON, mp3tag</li><li>비구조적: plain text</li></ul><p>파일이란</p><ul><li>byte의 나열</li><li>FS를 통한 상하구조</li><li>POSIX interface(이건 왜?)</li></ul><p>테이블</p><ul><li>처음부터 구조를 잘 짜야</li><li>같은 데이터도 타입문제(2 vs 2.0)</li><li>이력관리</li></ul><p>취합 문제</p><ul><li>필드가 다를 때</li><li>데이터 단위가 다름</li><li>같은 값인데 표현이 다름</li></ul><p>pandas</p><ul><li>data analysys + modeling for python</li><li>DataFrame: named column</li><li>R도 비슷한 data frame 지원</li></ul><p>DF in pySpark</p><ul><li>1.3부터 RDD 확장으로 지원</li><li>pandas, R의 DF와 같지만 분산환경</li><li>pandas DF와 convert 쉬움</li><li>pandas -&gt; pySpark시 driver 메모리 꽉 찰 수 있음 주의</li><li>RDD는 Scala구현체가 Python구현체보다 두 배 이상 빠름</li><li>DF는 RDD Scala보다 두 배쯤 빠르고 Py/Scala 비슷</li></ul><p>Apache Common Log Format의 데이터를 분석해보자</p><ul><li>컨텐츠 통계: status code, size</li><li>404 횟수</li></ul><ul><li>하루에 단일host는 얼마나?</li><li>하루에 요청은 얼마나?</li><li>호스트당 평균 요청은?</li><li>하루에 404는?</li></ul><p>로그 마이닝</p><ul><li>splunk를 통해 event log, disk error, network, cpu/memory usage 등 통계 및 분석</li></ul><p>read/write</p><ul><li>binary가 항상 빠르다</li><li>압축 알고리즘과 rw속도는 어느 정도 trade-off</li></ul><h3>Lecture 6. 구조적 데이터</h3><p>RDB</p><ul><li>relation: schema + instance</li><li>sparse에 약하다</li></ul><p>SQL 기초</p><ul><li>select, (inner, outer)join</li><li>join in Spark</li></ul><figure class="highlight py"><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">x = sc.parallelize([(<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">4</span>)])</span><br><span class="line">y = sc.parallelize([(<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;a&quot;</span>, <span class="number">3</span>)])</span><br><span class="line">x.join(y).collect</span><br><span class="line">// [(<span class="string">&#x27;a&#x27;</span>, (<span class="number">1</span>, <span class="number">2</span>)), (<span class="string">&#x27;a&#x27;</span>, (<span class="number">1</span>, <span class="number">3</span>))]</span><br><span class="line"></span><br><span class="line">x = sc.parallelize([(<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">4</span>)])</span><br><span class="line">y = sc.parallelize([(<span class="string">&quot;a&quot;</span>, <span class="number">2</span>)])</span><br><span class="line">x.leftOuterJoin(y).collect()</span><br><span class="line">// [(<span class="string">&#x27;a&#x27;</span>, (<span class="number">1</span>, <span class="number">2</span>)), (<span class="string">&#x27;b&#x27;</span>, (<span class="number">4</span>, <span class="literal">None</span>))]</span><br><span class="line"></span><br><span class="line">x.rightOuterJoin(y).collect()</span><br><span class="line">// [(<span class="string">&#x27;a&#x27;</span>, (<span class="number">1</span>, <span class="number">2</span>))]</span><br><span class="line"></span><br><span class="line">x = sc.parallelize([(<span class="string">&quot;a&quot;</span>, <span class="number">1</span>), (<span class="string">&quot;b&quot;</span>, <span class="number">4</span>)])</span><br><span class="line">y = sc.parallelize([(<span class="string">&quot;a&quot;</span>, <span class="number">2</span>), (<span class="string">&quot;c&quot;</span>, <span class="number">8</span>)])</span><br><span class="line">x.fullOuterJoin(y).collect()</span><br><span class="line">// [(<span class="string">&#x27;a&#x27;</span>, (<span class="number">1</span>, <span class="number">2</span>)), (<span class="string">&#x27;c&#x27;</span>, (<span class="literal">None</span>, <span class="number">8</span>)), (<span class="string">&#x27;b&#x27;</span>, (<span class="number">4</span>, <span class="literal">None</span>))]</span><br></pre></td></tr></table></figure><h3>Lab 2. 로그 분석</h3><p>Apache Log Format을</p><ol><li>정규식으로 나눠서</li><li>종류별 aggregation</li><li>key, value로 나눠서 plot</li><li>그리기 위해 2, 3번의 반복이 많아 분량에 비해 생각할꺼리는 적음</li><li>아마도 python 사용에 익숙치 않은 사람들을 위한 단순반복으로 추정</li></ol><p>tip.</p><p>저번 과제에 비해 데이터가 커서연산할 때 시간이 많이 걸리므로 REPL이나편한 python 툴을 꺼내놓고 기다리면서 다음 셀에서 어떻게 하면 될지 간단히테스트해보면 좋다.</p><p><img src="/images/big-data-with-spark-3-week/ptpython.png" alt=""></p><hr><ul><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week3Lec5.pdf">Lecture 5 slides</a></li><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week3Lec6.pdf">Lecture 6 slides</a></li></ul>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/spark/">spark</category>
      
      <category domain="http://seoh.blog/tags/bigdata/">bigdata</category>
      
      
      <comments>http://seoh.blog/2015/06/14/big-data-with-spark-3-week/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>ES7의 함수 bind 문법</title>
      <link>http://seoh.blog/2015/06/11/function-bind-syntax/</link>
      <guid>http://seoh.blog/2015/06/11/function-bind-syntax/</guid>
      <pubDate>Wed, 10 Jun 2015 17:02:01 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;어제 @wesbos가 &lt;code&gt;.bind()&lt;/code&gt;를 사용한 &lt;code&gt;querySelector&lt;/code&gt; 단축에 대한 js 소스를 캡쳐한 &lt;a href=&quot;https://twitter.com/wesbos/status/60834161617</description>
        
      
      
      
      <content:encoded><![CDATA[<p>어제 @wesbos가 <code>.bind()</code>를 사용한 <code>querySelector</code> 단축에 대한 js 소스를 캡쳐한 <a href="https://twitter.com/wesbos/status/608341616173182977">트윗</a>을 올렸다.</p><figure class="highlight js"><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">// 선택된 첫번째 엘리먼트 - $(&#x27;input[name=&quot;food&quot;]&#x27;)</span></span><br><span class="line"><span class="keyword">var</span> $ = <span class="built_in">document</span>.querySelector.bind(<span class="built_in">document</span>);</span><br><span class="line"><span class="comment">// 선택된 엘리먼트들의 배열 - $(&#x27;img.dog&#x27;)</span></span><br><span class="line"><span class="keyword">var</span> $$ = <span class="built_in">document</span>.querySelectorAll.bind(<span class="built_in">document</span>)</span><br></pre></td></tr></table></figure><p>여기에 @paul_irish가 비슷한 컨셉으로 이벤트 등록까지 할 수 있는 <a href="https://gist.github.com/paulirish/12fb951a8b893a454b32">bling.js</a>를 <a href="https://twitter.com/paul_irish/status/608433593061376003">리플</a>로 남겼다.</p><figure class="highlight js"><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">Node.prototype.on = <span class="built_in">window</span>.on = <span class="function"><span class="keyword">function</span>(<span class="params">name, fn</span>) </span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.addEventListener(name, fn);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 이하 생략</span></span><br></pre></td></tr></table></figure><p>이런 @thejameskyle가 흥미로운 <a href="https://twitter.com/thejameskyle/status/608438113703182338">리플</a>로 <a href="http://t.co/dQFFJHYZtZ">이런 소스</a>를 남겼다.</p><figure class="highlight js"><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">let</span> $ = ::<span class="built_in">document</span>.querySelector;</span><br><span class="line"><span class="keyword">let</span> find = Element.prototype.querySelectorAll;</span><br><span class="line"><span class="keyword">let</span> each = <span class="built_in">Array</span>.prototype.forEach;</span><br><span class="line"><span class="keyword">let</span> on = Node.prototype.addEventListener;</span><br><span class="line"><span class="comment">// 이하 생략</span></span><br></pre></td></tr></table></figure><p>생소한 문법이 등장했다. <code>::</code>는 어떤 의미일까?</p><hr><p>예전에 봤던 reactjs로 ES6를 접한 사람들이 하는 오해에 대한 글, <a href="https://reactjsnews.com/es6-gotchas/">ES6 Gotchas</a>가 생각났다.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;div onClick=&#123;<span class="built_in">this</span>.handleClick&#125; /&gt;</span><br></pre></td></tr></table></figure><p>이게 사실은</p><figure class="highlight js"><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="keyword">import</span> React <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">ComponentName</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="title">constructor</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">super</span>()</span><br><span class="line">    <span class="built_in">this</span>.handleClick = <span class="built_in">this</span>.handleClick.bind(<span class="built_in">this</span>)</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br></pre></td></tr></table></figure><p>이런 식으로 <code>this.handleClick</code>을 그대로 넘기는게 아니라 자동으로 bind해서 넘겨주는걸 react에서 해주기 때문에 사람들이 오해하기 쉽다는 뜻이다.babel에서는 (소위 ES7이라 부르는)실험 기능을 활성화할 수도 있는데, 그 중 데코레이터에 <a href="https://github.com/andreypopp/autobind-decorator">autobind decorator</a>를 붙일 수도 있다.<code>@autobind</code>를 메소드 혹은 함수 위에 붙이면 react처럼 bind해주는 기능을 한다. 그런데 이게 마음에 안 찼는지 <a href="https://github.com/zenparsing/es-function-bind">함수 Bind 문법</a>이라는 제안이나왔고, babel에도 <a href="http://babeljs.io/blog/2015/05/14/function-bind/">추가</a>되었으며 자세한 설명된 <a href="http://blog.jeremyfairbank.com/javascript/javascript-es7-function-bind-syntax/">포스팅</a>도 올라왔다. 위에 'ES6 Gotchas' 댓글에서 예제 소스를 빌려오자면,</p><ol><li><code>::a.b</code>는 <code>a::a.b</code>와 같다.</li><li><code>a::b.c</code>는 <code>b.c.bind(a)</code>와 같다.</li><li>그러므로 <code>::a.b</code>는 <code>a.b.bind(a)</code>와 같다.</li></ol><p>이제 다시 처음 소스의 <code>::document.querySelector</code>를 풀어보자면 <code>document.querySelector.bind(document)</code>라는 것을 알 수 있다.</p><hr><p>ES6/ES7(이제는 ES2015 or after라고 부르지만)에 대한 이야기를 자주 하는 이유는 재미있고 내가 관심 있는 분야라서도 그렇지만,이런 기술들은 나 혼자 편하다고 해서 쓸 수 있는 것이 아니다. 같이 일하는 사람들이 필요성에 대해 공감하고 익숙해야 쓸 수 있다.그래서 보다 많은 사람들이 관심을 가지고 익숙해졌으면하는 바람이 있다. (물론 나는 현재 무직이라 같이 일하는 사람이 없지만 나중을 위해)</p><p>JavaScript라는 언어의 문법 자체는 심플한 편이지만, 거지같은 실행환경 때문에 <code>this</code>가 뭐냐 언제 결정되느냐는 질문을 하도 많이 받아서<a href="http://devthewild.tumblr.com/post/73112749480/var-that-this">&quot;var that = this&quot; 같은 자바스크립트 스코프에 대한 이해</a>라는StackOverflow의 답변을 번역해 글을 쓴 적도 있다. 어차피 웹이라는 거대한 플랫폼에서 자유로운 개발자는 많지 않고, 발을 디딘 사람들 중 대부분은 프론트를 한 번쯤 만지게 된다.피할 수 없다고 즐기는 건 변태지만, 최대한 생산성을 높여서 괴로움을 줄이길 바란다.</p>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/ES7/">ES7</category>
      
      <category domain="http://seoh.blog/tags/babel/">babel</category>
      
      
      <comments>http://seoh.blog/2015/06/11/function-bind-syntax/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Spark로 빅데이터 입문, 1-2주차 노트</title>
      <link>http://seoh.blog/2015/06/10/big-data-with-spark-1-2-week/</link>
      <guid>http://seoh.blog/2015/06/10/big-data-with-spark-1-2-week/</guid>
      <pubDate>Wed, 10 Jun 2015 12:18:51 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;edX에서 &lt;a href=&quot;https://courses.edx.org/courses/BerkeleyX/CS100.1x/1T2015&quot;&gt;Spark로 빅데이터 입문(Introduction to Big Data with Apache Spark)&lt;/a&gt;을</description>
        
      
      
      
      <content:encoded><![CDATA[<p>edX에서 <a href="https://courses.edx.org/courses/BerkeleyX/CS100.1x/1T2015">Spark로 빅데이터 입문(Introduction to Big Data with Apache Spark)</a>을듣고 있다. UC Berkeley의 Anthony Joseph 교수가 진행하는 수업으로, 실제 데이터를 가지고 과제4개를 진행하면서 Spark로 빅데이터 분석하는 방법을 배운다고 하는데, 수업 난이도 자체는 높지 않다.대상은 Python 경험자로 분산 컴퓨팅/Spark에 대한 지식은 없어도 된다고 되어있다. 환경 설정도Jupyter(IPython Notebook의 새 이름)와 PySpark가 이미 세팅된 환경을 Vagrant로 제공해주는데,Vagrant의 이름만 알고 있는 정도였지만 동영상에서 OS별로 제공하는 동영상을 보고 따라하는데 무리는 없었다.</p><p>실습은 노트북 파일에서 비어있는 부분을 채우면서 진행하면 되는데, 의도에 대한 설명은 코드 위에 충분히자세하게 되어있고 단계별 진행이다보니 정확히 읽지 않아도 코드를 보면서 따라가면 유추가 가능하다.단순히 유행어(Buzzword) 이상으로 Big Data나 Spark에 대해 배워보고 싶은 사람들에게 추천할 겸,몇달 뒤에 잊어먹을 나 자신을 위해서 기록을 남겨본다.</p><h1>1주차</h1><p>환경설정: Jupyer + PySpark가 세팅된 vagrant 올리기</p><p>수업 목표</p><ul><li>Data Science를 배워보자</li><li>실제 데이터를 다루는 과정</li><li>Spark(w/ mllib)를 써보자</li></ul><h3>Lecture 1. Big Data와 Data Science 소개</h3><ul><li>데이터 분석의 역사</li><li>원인 ≠ 상관관계</li><li>충분한 데이터가 필요</li><li>현재를 분석하는건 쉽고 미래를 예측하는건 어렵다</li><li>사건에 대한 중요한 요인을 모두 알고 있어야한다</li><li>(MySpcae 사례를 통한 페이스북 유행시기 추정 -&gt; 실패)</li></ul><p>빅데이터는 어디에서?</p><ul><li>사용자들이 올리는 컨텐츠들(웹/모바일) - 페이스북, 인스타그램, 옐프</li><li>생체정보/과학쪽 연산</li></ul><p>그래프 데이터</p><ul><li>많은 데이터들이 그래프 구조</li><li>SNS, 네트워크 등</li></ul><p>사람이 보기에 너무 많은 정보</p><ul><li>아파치 서버 로그</li><li>IoT의 센서 측정기록</li></ul><p>빅데이터를 어떻게 다룰까</p><ul><li>Crowdsourcing</li><li>Physical modeling</li><li>Sensing</li><li>Data Assimilation</li></ul><p>많은 사람들에게서 정보를 얻어 분석한 다음에 중요한 정보를 시각화</p><h3>Lecture 2. Data Science는 어떻게? 데이터 준비하기</h3><ul><li>코딩 + 도메인 지식만으로는 잘못된 분석의 가능성</li><li>Data Science = 코딩 + 도메인 + 통계지식</li></ul><p><a href="http://drewconway.com/zia/2013/3/26/the-data-science-venn-diagram">The Data Science Venn Diagram</a></p><p>Database와 Data Science의 차이</p><ul><li>데이터가 중요하다 / 싸다</li><li>원자성이 중요 / 결과적으로 유지만되면 오케이</li><li>과거를 조회 / 미래를 예측</li><li>정해진 복잡도 / 하다보면 됨</li></ul><p>방법</p><ul><li>Jim Gray 모델 vs Ben Fry 모델 vs Jeff Hammerbacher 모델</li></ul><p>어려움</p><ul><li>실제 데이터는 너무 더러움</li><li>이전에 발견한 패턴/가설이 선입견</li></ul><p>준비: ETL</p><ul><li>extract: 데이터 추출</li><li>transform: 가공</li><li>load: 모아놓는다</li></ul><h1>2주차</h1><h3>Lecture 3. 빅 데이터, Spark</h3><p>왜 필요한가?</p><ul><li>페이스북의 하루 로그는 60TB</li><li>이제 머신 하나에서 다룰 수 없다</li></ul><p>거대한 문서에서 단어를 세는 예제</p><ul><li>머신 n-to-1<ul><li>문단 별 단어 카운트는 n개에서 나눠서 해도 된다</li><li>합치는 작업을 1개에서 한다면 병목</li></ul></li><li>머신 n-to-n<ul><li>합치는 작업도 단어별로 n개에서 나눠서</li><li>이게 Map/Reduce 개념</li></ul></li><li>문제<ul><li>머신끼리 데이터 보내는건 느리다</li><li>하나가 실패하면? -&gt; 그 머신만 다시 시작</li><li>하나가 느리면? -&gt; 하나 더 돌리고 먼저 끝난걸 선택</li></ul></li><li>문제 2<ul><li>작업을 반복해서할 때 디스크에 저장하면 I/O가 병목</li><li>메모리에 올려서 작업하면? -&gt; Spark</li></ul></li></ul><p>Spark</p><ul><li>위에서 말한 실패하거나 느린 노드를 자동으로 처리</li><li>Core와 컴포넌트 4개 Spark SQL, Spark Streaming, MLlib, GraphX로 구성</li><li>disk에 넣기 위해 serialization/deserialization 할 필요가 없으므로 100배 이상 빠르다</li></ul><p>특징</p><ul><li>다양한 경우에 쓸 수 있는 엔진</li><li>리니지(immutable을 이용한 이력관리)의 lazy eval</li></ul><h3>Lecture 4. Spark 기초</h3><ul><li>어플리케이션(driver)에서 SparkContext로 시작</li><li>worker는 클러스터 혹은 로컬 스레드</li><li>분산된 worker의 추상화가 RDD</li></ul><p>맨 처음 실행하면</p><ul><li>SparkContext 객체의 인스턴스 <code>sc</code> 생성</li><li><code>sc</code>로 로컬의 worker 접근, 혹은 hdfs, mesos 접근</li></ul><p>RDD는</p><ul><li>만들어지면 불변</li><li>transform되는 과정(lineage)을 추적해서 재사용 가능</li><li>파티션을 설정하면 그만큼 병렬처리</li></ul><p>연산은</p><ul><li>transformation, action 두 종류</li><li>transform: lazy eval, action까지 미뤄둠</li><li>persis(cache)는 메모리/디스크 저장</li></ul><figure class="highlight python"><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">data = [<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">rDD = sc.parallelize(data, <span class="number">4</span>)</span><br></pre></td></tr></table></figure><p>혹은</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">file = sc.textFile(<span class="string">&quot;README.md&quot;</span>, <span class="number">4</span>)</span><br></pre></td></tr></table></figure><p>transformation</p><ul><li>map, filter, distinct, flatMap,</li></ul><p>action</p><ul><li>바로 계산</li><li>reduce, take, collect, takeOrdered</li></ul><figure class="highlight python"><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">lines = sc.textFile(<span class="string">&quot;...&quot;</span>, <span class="number">4</span>)</span><br><span class="line">comments = lines.<span class="built_in">filter</span>(isComment)</span><br><span class="line"><span class="built_in">print</span> lines.count(), comments.count()</span><br></pre></td></tr></table></figure><p>lines 연산을 한번, lines+comments 연산을 한번해서 중복. 그럴 때는 중간에 <code>lines.cache()</code></p><p>key-value transformation</p><ul><li>reduceByKy, sortByKe, groupByKey</li><li>groupByKey는 데이터가 이동하므로 네트워크/대량에서는 주의</li></ul><figure class="highlight python"><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">rdd // [(<span class="number">1</span>,<span class="number">2</span>), (<span class="number">3</span>,<span class="number">4</span>), (<span class="number">3</span>,<span class="number">6</span>)]</span><br><span class="line">rdd.reduceByKey(<span class="keyword">lambda</span> a, b: a + b) // [(<span class="number">1</span>,<span class="number">2</span>), (<span class="number">3</span>,<span class="number">10</span>)]</span><br></pre></td></tr></table></figure><p>closure</p><ul><li>broadcase: driver가 쓰면 worker들이 읽기 가능</li><li>accumulator: worker들이 쓰기만 가능 driver만 접근가능</li></ul><h1>Lab 1. Spark 과제</h1><p>구텐베르크 프로젝트에서 셰익스피어 전집의 데이터로 단어 세기</p><ul><li>특수문자 제거</li><li>띄어쓰기로 단어 구분</li><li>중복 제거를 위해 소문자로 변환</li><li>최빈 15개 단어</li></ul><hr><p>참고로 강의노트(PDF)가 공개되어있다.</p><ul><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week1Lec1.pdf">Lecture 1 slides</a></li><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week1Lec2.pdf">Lecture 2 slides</a></li><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week2Lec3.pdf">Lecture 3 slides</a></li><li><a href="https://courses.edx.org/c4x/BerkeleyX/CS100.1x/asset/Week2Lec4.pdf">Lecture 4 slides</a></li></ul>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/spark/">spark</category>
      
      <category domain="http://seoh.blog/tags/bigdata/">bigdata</category>
      
      
      <comments>http://seoh.blog/2015/06/10/big-data-with-spark-1-2-week/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>나는 왜 크롬을 쓰는가</title>
      <link>http://seoh.blog/2015/06/07/why-i-cannot-use-safari-and-firefox/</link>
      <guid>http://seoh.blog/2015/06/07/why-i-cannot-use-safari-and-firefox/</guid>
      <pubDate>Sat, 06 Jun 2015 17:16:58 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;사람들이 크롬하면 생각하는 대표적인 장단점으로는 강력한 확장과 강력한 메모리(흡수)가 있다.
아무 이유 없이 수많은 프로세스와 수많은 메모리를 잡아먹지는 않을테니 그 트레이드오프가 내부적으로는
앞으로 쓸 이야기와 관련있을 수도 있지만, 내가 크롬</description>
        
      
      
      
      <content:encoded><![CDATA[<p>사람들이 크롬하면 생각하는 대표적인 장단점으로는 강력한 확장과 강력한 메모리(흡수)가 있다.아무 이유 없이 수많은 프로세스와 수많은 메모리를 잡아먹지는 않을테니 그 트레이드오프가 내부적으로는앞으로 쓸 이야기와 관련있을 수도 있지만, 내가 크롬을 쓰는 이유는 (직접적으로)이것들 때문은 아니다.일단 내 패턴에서 브라우저의 기본 조건은 다음과 같다.</p><ul><li>죽는 건 상관없지만 죽은 뒤에 탭 목록이 유지될 것</li></ul><p>패턴이라는 게 별것 없다. 트위터, RSS, 뉴스레터에 올라온 링크들을 브라우저에 최대한(약 50개)으로올려놓고 빠르게 훑어본 뒤 필요 없다고 판단된 것들은 닫고 정독할 것들을 쌓아둔다. 예전에는 그냥 닫거나포켓에 넣거나 둘 중 하나였는데, 포켓이 반응속도가 느려진 뒤로 포켓을 거치지 않고 그냥 브라우저에띄워놓고 있다. 다양한 곳에서 하루에 세 자리 단위의 링크들을 띄우고 정리하다보니 어디까지 읽었는지,어떤 것을 정독하려고 판단했는지의 목록이 사라지면 그것을 다시 복구하는 것만 해도 경험상 두세 시간 이상은걸리게 되니 탭 목록이 사라지는데 매우 민감해졌다. 물론 공식적으로 사파리/파이어폭스도 탭 목록을유지해준다.</p><p>사파리에서는 다시 열었을 때, 마지막 세션에서 모든 윈도우를 다시 자동으로 열어주는 옵션도 있고, 혹여저 옵션을 선택하지 않았거나 비정상적으로 종료되었을 때 마지막 세션의 모든 윈도우를 다시 열게 해주는기능이 존재한다. 그리고 Session 등 주기적으로 세션을 백업해두는 확장도 존재하긴 한다. 다만,비정상적으로 종료당하는 일이 그리 많지는 않은데 그럴 때 앞에서 언급한 기능들에서 제대로 목록을살려주는 경우가 별로 없다. 맥에서 가장 미려하고 반응속도도 좋은 사파리라 사파리나 OS X의 메이저업데이트때마다 항상 재도전을 하고 있지만 항상 실패했다. 물론 10.11이나 Safari 9이 나오면 또도전할 예정이다. 하지만 그리 큰 기대를 하진 않는다.</p><p>파이어폭스도 마찬가지지만 비정상 종료의 모습이 약간 다르다. 사파리는 &quot;뿅! 사파리가 사라졌습니다&quot;하면서 갑자기 튕긴다면, 파이어폭스는 갑자기 &quot;나 죽어어어어어어어어&quot;하면서 CPU 점유율이 100%를넘어가며 주변에 민폐를 끼쳐서 어쩔 수 없이 강제종료하게 만든다. IE 6, 7 시절부터 쓰기 시작해서크롬으로 넘어가기 전에 메인으로 쓰다가, 크롬이 지나치게 무거워져서 돌아간 게 20대 후반 버전이었던것으로 기억하는데 파이어폭스도 그때부터 메이저 버전때마다 도전했지만 항상 저런 증상 때문에 실패했다.Session Manager 등 확장들을 찾아봤지만 평소에도 느리게 만들고 구동시간을 느리게 만들 뿐이었다.</p><hr><p>사파리가 다른 두 브라우저에 비해 개발도구의 기능은 <strong>매우</strong> 떨어지나, 메모리/배터리 효율 면에서비교하기 어려울 정도로 좋다는 장점이 있고, 파이어폭스는 개발도구로써 최신 기술이 가장 먼저 들어가고<a href="https://hacks.mozilla.org/2014/07/event-listeners-popup-media-sidebar-cubic-bezier-editor-more-firefox-developer-tools-episode-33/">Event listeners popup</a>처럼 다른 브라우저에서 찾기 힘든 편리한 기능 등이 있다.</p><p>저 브라우저들이 내 머신들에서만 같은 문제를 발생하는 것일 수도 있고, 하루에 몇백 개의 링크를 훑어봐야하는사람이 많지도 않을테니 누군가에게 동의를 구할 수도 없고 그럴 생각도 없는 이유지만, 이것이 내가 크롬을쓸 수 밖에 없는 이유다.</p>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/chrome/">chrome</category>
      
      
      <comments>http://seoh.blog/2015/06/07/why-i-cannot-use-safari-and-firefox/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>흥미로운 ECMAScript 제안들</title>
      <link>http://seoh.blog/2015/05/31/interesting-es-proposal/</link>
      <guid>http://seoh.blog/2015/05/31/interesting-es-proposal/</guid>
      <pubDate>Sun, 31 May 2015 07:58:22 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;RSS에 글이 뜸해질 때는 &lt;a href=&quot;http://www.coven.link/&quot;&gt;Coven&lt;/a&gt;을 보는데, &lt;a href=&quot;https://news.ycombinator.com/&quot;&gt;Hacker News&lt;/a&gt;, &lt;a href=&quot;http://</description>
        
      
      
      
      <content:encoded><![CDATA[<p>RSS에 글이 뜸해질 때는 <a href="http://www.coven.link/">Coven</a>을 보는데, <a href="https://news.ycombinator.com/">Hacker News</a>, <a href="http://www.reddit.com/r/programming">/r/programming</a>(Reddit의 프로그래밍 서브레딧- 게시판 개념) 등 사이트 4개의 추천 상위 글을 실시간으로 가져오는 곳이다.다른 곳에도 공유하긴 했지만, 여기 올라온 글 중에서 흥미로운 ES의 제안(제안-초안-후보-마감의 단계 중 첫 번째)에 해당하는 것 2개를 소개하려고 한다.</p><h2>1. <a href="https://docs.google.com/document/d/1Qk0qC4s_XNCLemj42FqfsRLp49nDQMZ1y7fwf5YjaI4/preview">&quot;use strong&quot; mode in ES6</a></h2><p>ES5의 &quot;use strict&quot;를 포함하는 개선된(빡쎈) 버전이다. V8의 경우 예측할 수 있는 경우에는 최적화된 컴파일러를, 아닌 경우에는 일반 컴파일러를 실행한다고되어있는 <a href="https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#some-v8-background">글</a>이 있었는데, 다른 구현체들의정확한 internal은 잘 모르겠지만 컨셉 자체는 다른 곳에도 적용가능해보인다. 다시 말해서, 프로그램 흐름과 객체 상태를 제약해서 유지보수를 편하게 하고성능을 좋게 한다는 아이디어에 대한 구체적인 제약들이다. 대표적으로 <code>var</code>와 <code>delete</code>를 금지하는 문법상 제약이 있다. <code>var</code>를 금지하는 대신 <code>let</code>을사용해서 lexical scope의 범위를 줄여서 컴파일러나 사람이 생각해야하는 양을 줄이겠다는 것으로 보인다. 그리고 <code>delete</code>를 금지하면 객체의 형태가 고정(seal)되어 형태에 대한 정보를 재사용하고 예측가능해진다.(참고, ES6의 <code>Object.freeze</code>와 <code>Object.seal</code>의 차이에 대한 <a href="http://stackoverflow.com/questions/21402108/difference-between-freeze-and-seal-in-javascript">답변</a>)</p><h2>2. <a href="https://docs.google.com/file/d/1uEVcOgJIMsHjN1vypKKyfmDRg_bz5cKXpo0v4Nc0q8NfqKolBeSDHIj8z9GS8A4EiMpZ8QQ3l87Q_wF3/preview">ES2016 Observable</a></h2><p>ES6의 Generator와 ES7의 async/await를 합치면 비슷한 구현은 가능하지만, async generator의 경우에 IO에는 적합하지만 Push(Event, WebSocket)등에는 적합하지 않는다. 즉 async는 producer를 observable은 consumer를 만들 때 적합하다(하지만 이해를 잘못한 것인지 슬라이드에는 반대로 되어있다).사실 이게 들어갈 확률은 매우 적어 보이지만, 요즘 듣고 있는 Coursera의 <a href="https://class.coursera.org/reactive-002/">reactive programming</a>에서Rx에 대한 내용이 나오다 보니 그쪽으로 관심이 생겨서 RxJS도 써보고 싶은데 마침 이 제안을 발견해서 기억에 남았다.</p><hr><p>ps 1, 이것들 말고도 hn/reddit에 추천 상위로 올라온 게 더 있지만 재미가 없었는지 키워드도 기억나지 않는 게 많다.</p><p>ps 2, 흥미로운 것들을 발견하면 실시간으로 트위터에 올리고 있다. <a href="https://twitter.com/@devthewild">@devthewild</a></p><p>ps 3, 구직중</p>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/ES6/">ES6</category>
      
      <category domain="http://seoh.blog/tags/observable/">observable</category>
      
      
      <comments>http://seoh.blog/2015/05/31/interesting-es-proposal/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Callback에서 Future로(그리고 Functor, Monad)</title>
      <link>http://seoh.blog/2015/05/28/callback-to-future-functor-applicative-monad/</link>
      <guid>http://seoh.blog/2015/05/28/callback-to-future-functor-applicative-monad/</guid>
      <pubDate>Thu, 28 May 2015 00:39:49 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;h3&gt;Translation of &amp;quot;&lt;a href=&quot;http://tech.pro/blog/6742/callback-to-future-functor-applicative-monad&quot;&gt;From callback to (Fut</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><h3>Translation of &quot;<a href="http://tech.pro/blog/6742/callback-to-future-functor-applicative-monad">From callback to (Future -&gt; Functor -&gt; Monad)</a>&quot; into Korean, under the same license as the original.</h3></blockquote><h2>동기</h2><p>함수형 프로그래밍에서 기본개념은 <strong>조합(composition)</strong> 이다. 간단히 설명해서, 단순한 것들을 엮어서 더복잡한 것을 만들 수 있고 그 결과를 다시 엮어서 더 복잡한 것을 만들 수도 있다. 함수의 의미나 리턴값이무엇인지만 알고 있으면 조합으로 무엇이든 만들어낼 수 있다.</p><p>Node.js를 써봤으면 아래와 같은코드를 본 적이 있을 것이다.</p><figure class="highlight js"><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">fs.readFile(<span class="string">&#x27;...&#x27;</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (err) <span class="keyword">throw</span> err;</span><br><span class="line">  ....</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>위의 코드는 전형적인 CPS(continuation-passing style) 함수이다. <code>fs.readFile</code>이라는 CPS함수는 계속(continuation) 진행될 콜백을 추가 파라미터로 받는다. 이 CPS가 끝나면 호출한 곳에 값을반환하는게 아니라 계속 함수, 콜백에 계산 결과를 넘겨준다.</p><p>나는 콜백을 쓰는걸 꺼리진 않는다. 사실 콜백은 부수효과를 표현하거나 이벤트 알림같은 것을 다룰 때훌륭하다. 그렇지만 그걸로 흐름을 관리하기 시작하면 함정에 빠진 것이다. 왜냐면 조합할 수 없기 때문에.</p><p>자, 생각해보자. &quot;위에 나온 함수의 **표기(denotation)**나 리턴값은 무슨 의미일까?&quot; 답은<code>undefined</code>이다. undefined라는건 테스트할 때 실제로 undefined인지 확인하는 용도 이외에는 쓸데가 없다.</p><p>콜백 안에서 다른 실행 흐름으로 넘어갈 때 일방통행이라는게 문제다.</p><p>물리학에서 유명한 블랙홀처럼 콜백을 생각해보자:</p><blockquote><p>블랙홀은 수학적으로 정의된 지역이다. 강한 인력이 작용하거나, 어떤 티끌이나 전자기적 파장조차빠져나갈 수 없는</p></blockquote><p>그 중에서도</p><blockquote><p>어떤 빛도 반사하지 않는 등 블랙홀은 많은 부분에서 이상적인 검은 물체처럼 작용한다.</p></blockquote><p>콜백 함수 또한 흐름에서의 어떤 것도 반사하지 못한다.</p><p>나중에 첫번째 콜백에 들어갈 때 다른 콜백 스타일 함수를 쓸 수 있는데, 그때는 두번째 <strong>흐름</strong>을 잃게되고 다른 구멍에 빠지게 된다. 콜백을 쓰면 쓸 수록 지옥에 빠지게 된다.</p><p>그럼 블랙홀에 빠지지 않고 코드를 진행할 수는 없을까?</p><p>답은 <strong>조합</strong>이다. 하지만 조합을 사용하려면 일단 CPS 함수가 어디로도 돌아갈 수 없다는 사실을알아야하고, 함수로부터 뭔가를 받아와야한다. 그러니 어떻게든 함수가 뭔가를 반환하게 만들어야한다. 어떤값이 반환될까? 이게 이 글의 동기이다.</p><p>이미 자바스크립트에서의 해답을 알고 있을 수 있다. 하지만 계속 이 글을 읽도록, 강하게, 추천한다.지시적인(즉 함수형) 생각의 힘을 보게 될 것이고, 깔끔하고 간결한 해답을 어떻게 사용할지 보게 될 것이다.</p><h2>future로 입문</h2><p>파일 읽기, 네트워크 요청, DOM 이벤트, 이런 함수들의 공통점은 뭘까?</p><p>이 함수들은 <em>즉시</em> 완료되지 않는 것들이다. 즉, (보통 함수들을 다루는 식으로는) 현재 프로그램흐름에서 저 함수들이 완료될 때까지 기다릴 수 없다는 뜻이다. 그래서 _future_를 설명할 것이다.</p><p>그래서 특별한 반환 타입, 나중에 결과를 만들어준다고 명시하는 <code>Future</code>를 만들어보자. 요점은 다른함수들로 넘길 수 있는 1등급 클래스 값을 사용하는 것이다.</p><p>Future는 무슨 의미일까? 특정 시간(0이 될 수도 있다) 후에 발생할 것이라고 명시해놓은 값이다.그 시간은 우리가 x초 후라고 말하는 것처럼 명시적인 시간이 될 수도 있지만, Future 2개가 완료된 후혹은 Future 하나가 완료된 뒤 다른 Future 완료될 때처럼 상대적인 개념일 수도 있다.</p><p>여기서 중요한 점은: <strong>Future의 결과는 항상 불변값이다.</strong></p><p>즉, 완료 값을 어떤 방법으로든 변경할 수 없다. 이 제약으로 구현 뿐만 아니라 의미론에 대한 추론도간단해진다.</p><p>Future는 일회용의 간단한 상태머신처럼 구현될 수 있다. 이 머신은 <em>대기</em> 로 시작했다가 <em>완료</em> 가 된후에 멈춘다. 한번 완료되면 계속 완료상태에 고정된다.</p><p>내부적으로 <code>Future</code>는 콜백에 여전히 의존하고 있지만, 그 콜백들이 컨트롤 흐름 매커니즘을 방해하지는않는다. 대신 올바른 목적으로만 사용된다, 이벤트 알림.</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">Future</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 대기중인 구독들을 저장하는 리스트</span></span><br><span class="line">  <span class="built_in">this</span>.slots = [];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 완료를 알린다</span></span><br><span class="line">Future.prototype.ready = <span class="function"><span class="keyword">function</span>(<span class="params">slot</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">this</span>.completed) slot(<span class="built_in">this</span>.value);</span><br><span class="line">  <span class="keyword">else</span> <span class="built_in">this</span>.slots.push(slot);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 간단한 로그 유틸리티</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">logF</span>(<span class="params">f</span>) </span>&#123;</span><br><span class="line">  f.ready( <span class="function"><span class="params">v</span> =&gt;</span> <span class="built_in">console</span>.log(v) );</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Future를 완료시키는 외부 인터페이스로 메소드가 있다.</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line">Future.prototype.complete = <span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// 불변성 보장</span></span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">this</span>.completed)</span><br><span class="line">    <span class="keyword">throw</span> <span class="string">&quot;이미 완료된 Future는 완료시킬 수 없다.&quot;</span></span><br><span class="line"></span><br><span class="line">  <span class="built_in">this</span>.value = val;</span><br><span class="line">  <span class="built_in">this</span>.completed = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 구독들에게 알림</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>, len=<span class="built_in">this</span>.slots.length; i&lt; len; i++) &#123;</span><br><span class="line">    <span class="built_in">this</span>.slots[i](val);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 모두 실행되면 이제 필요없다.</span></span><br><span class="line">  <span class="built_in">this</span>.slots = <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Future의 가장 간단한 예제로 어떤 값으로 <em>즉시</em> 완료시켜보자. 그 역할을 <code>unit</code>이란 메소드를만들어보자.</p><figure class="highlight js"><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">// unit: Value -&gt; Future&lt;Value&gt;</span></span><br><span class="line">Future.unit = <span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line">  fut.complete(val);</span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">logF( Future.unit(<span class="string">&#x27;hi now&#x27;</span>) );</span><br></pre></td></tr></table></figure><p>코드에 대해 간단히 설명하기 위해 <em>타입 표기(type annotation)</em> 를 사용했다.</p><p><code>unit: Value -&gt; Future&lt;Value&gt;</code>를 풀어보면 1- <code>unit</code>은 함수고, 2- 제네릭 타입 <code>Value</code>를입력으로 받으며, 3- 제너릭 타입을 가진 <code>Future</code> 인스턴스를 리턴한다. 여기서 타입 정보는 중요하지않으므로 <code>Value</code>라는 제너릭은 신경쓰지 않아도 된다.</p><p>다음 예제는 특정 시간이 지나고 완료되는 값이다.</p><figure class="highlight js"><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="comment">// delay: (Value, Number) -&gt; Future&lt;Value&gt;</span></span><br><span class="line">Future.delay = <span class="function"><span class="keyword">function</span>(<span class="params">v, millis</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> f = <span class="keyword">new</span> Future();</span><br><span class="line">  <span class="built_in">setTimeout</span>(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    f.complete(v);</span><br><span class="line">  &#125;, millis);</span><br><span class="line">  <span class="keyword">return</span> f;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">logF( Future.delay(<span class="string">&#x27;안녕, 이건 5초 걸린다&#x27;</span>, <span class="number">5000</span>) );</span><br></pre></td></tr></table></figure><p><code>delay</code>의 결과는 주어진 값만큼의 시간이 지난 뒤에 완료되는 Future다.</p><p>readFile 예제로 돌아가서, 이제 CPS 함수 대신에 Future를 리턴하는 함수를 사용할 수 있다.</p><figure class="highlight js"><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">var</span> fs = <span class="built_in">require</span>(<span class="string">&#x27;fs&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// readFileF: (String, Object) -&gt; Future&lt;String&gt;</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">readFileF</span>(<span class="params">file, options</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> f = <span class="keyword">new</span> Future();</span><br><span class="line">  fs.readFile(file, options, <span class="function"><span class="keyword">function</span>(<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// 에러는 잠시 후에 다루겠다</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(err) <span class="keyword">throw</span> err;</span><br><span class="line">    f.complete(data);</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> f;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">logF( readFileF(<span class="string">&#x27;test.txt&#x27;</span>, &#123;<span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span>&#125;) );</span><br></pre></td></tr></table></figure><p><code>readFileF</code>의 결과는 인자로 받은 파일 이름의 내용을 잡고 있는 Future가 된다.</p><h2>Future 다루기: 첫번째 게스트</h2><p><code>Future</code>를 결과적으로 함수의 결과를 잡고 있는 마법 상자처럼 생각할 수도 있다.</p><p>뭔가 쓸모있는 것을 하려면 Future 타입에서 쓸모있는 연산들을 제공해야한다. 그러지 않는다면필요없이 또 다른 <code>undefined</code>를 만든 것과 다를 바 없다.</p><p>그러면 어떤 연산을 Future에서 제공해야할까?</p><p>Future 상자에서 잡고 있는 값에 어떤 연산을 하고 싶을 때 (function map을 줄인)<code>fmap</code>을 호출할것이다.</p><p><code>fmap</code>의 예제를 보자. 여기서 Future는 텍스트 파일의 내용을 잡고 있고, 이 내용의 길이를 계산하려고한다.</p><figure class="highlight js"><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">var</span> textF = readFileF(<span class="string">&#x27;test.txt&#x27;</span>, &#123;<span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span>&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// fmap: (Future&lt;String, (String -&gt; Number)&gt; -&gt; Future&lt;Number&gt;)</span></span><br><span class="line"><span class="keyword">var</span> lengthF = textF.fmap( <span class="function"><span class="params">text</span> =&gt;</span> text.length );</span><br><span class="line">logF( lengthF );</span><br></pre></td></tr></table></figure><p><code>lengthF</code>의 뜻은 인자로 받은 Future가 잡고 있는 파일 내용의 길이를 잡고 있는 Future다.</p><p>일반화를 해보자면, <code>fmap</code>은 인자를 둘 받는데, 하나는 값을 잡고 있는 Future고 하나는 일반값을다루는 매핑 함수다. 입력으로 받은 Future의 결과물에 매핑 함수를 적용한 결과를 잡고 이는 Future가결과로 나온다. 받은 Future와 결과 Future는 둘 다 동시에 완료된다.</p><p>정확하진 않지만, 이렇게 표현할 수 있다</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fmap( Future&lt;value&gt;, func ) = Future&lt; func(value) &gt;</span><br></pre></td></tr></table></figure><p><code>fmap</code>은 몇줄만으로 구현할 수 있다.</p><figure class="highlight js"><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">Future.prototype.fmap = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line">  <span class="built_in">this</span>.ready(<span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">    fut.complete( fn(val) );</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>현재 Future가 완료되었을 때 결과로 나온 Future도 완료된다. 그 때 매핑 함수를 적용시킨다.</p><p>위의 예제에서는, 파일 내용을 잡고 있는 Future를 내용 길이를 잡고 있는 다른 Future로 변이시켰다.</p><p>어디서 들어본 말 같지 않은가? 잘 알고 있는 자바스크립트 Array의 <code>map</code> 메소드와 꽤 비슷하다.실제로 정확히 같은 개념이다.</p><ul><li>Array  타입은 여러 값들을 잡고 있는 박스다</li><li>Future 타입은 완료될 값을 잡고 있는 박스다</li><li>Array.map(...) 은 Array 박스 안의 값들을 변이시켜서, 변이된 값들을 잡고 있는 다른 Array 박스를 돌려준다</li><li>Future.fmap(...)은 Future 박스 안의 값을 변이시켜서, 변이된 값을 잡고 있는 다른 Future 박스를 돌려준다</li></ul><p>Array와 Future 타입 둘 모두 포함되는 <strong>Functor</strong>라는 첫번째 게스트가 등장했다. 일반 함수를 하나받아서 안에 무엇을 가지고 있든 그것이 변이된 결과를 표현하는 다른 인스턴스를 만들어내는 타입이다.</p><ul><li>다른 타입을 감싸는 컨텍스트처럼 작동할 수 있는 타입이고</li><li>내부에 있는 것을 일반 함수에 적용시킬 수 있다면</li></ul><p>Array와 Future가 아니더라도 그게 무엇이든간에 그 타입을 <strong>Functor</strong>라고 부를 수 있다.</p><p>이제 Future를 다른 Future로 매핑할 수 있다. 이제 일반값을 다루듯이 Future를 직접적으로 다루는함수를 만들 수 있다는 뜻이다. <code>textF.fmap( c =&gt; c.length )</code>처럼 호출하는 대신에 Future를직접 다루는 <code>lengthF</code>라는 특별한 종류의 함수를 만들 수도 있다.</p><figure class="highlight js"><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">// lengthF: Future&lt;String&gt; -&gt; Future&lt;Number&gt;</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">lengthF</span>(<span class="params">strF</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> strF.fmap( <span class="function"><span class="params">s</span> =&gt;</span> s.length )</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>파일 길이를 읽는 예제를 흔히 보던 방법처럼 다시 작성할 수 있게 되었다.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nbCharsF = lengthF( readFileF(<span class="string">&#x27;...&#x27;</span>) )</span><br></pre></td></tr></table></figure><p><code>lengthF</code>를 <em>lift된</em> 함수라고 부른다. Functor같은 _박스 타입_을 다루는 함수를 <strong>lift</strong>한다는것은 일반값을 다루는 함수를 박스 타입을 다루는 함수로 만든다는 뜻이다. 여기에서는 문자열을 다루는 함수<code>length(String)</code>를 lift해서 Future를 다루는 함수<code>lengthF( Future&lt;String&gt; )</code>로 lift했다.</p><p>일반화된 <code>lift1</code>(인자를 하나만 받아서 lift하는 함수)를 정의해보자.</p><figure class="highlight js"><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">Future.lift1 = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="params">fut</span> =&gt;</span> fut.fmap(fn);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>비동기 실행을 일반 함수 실행처럼 만들어주는 간단한 추상 함수다. 위에서 <code>lengthF( readFileF('...') )</code>는<code>readFileF</code>와 <code>lengthF</code>를 조합해서 비동기 연산을 현재의 흐름을 떠나지 않고 실행할 수 있다.</p><h2>인자를 여러개 받는 함수는 어떻게? (두번째 게스트?)</h2><p>질문에 대답하기 전에 잠시 기초지식에 대해 생각해보자: Future 박스가 잡을 수 있는 타입에는 뭐가있을까? Future는 모든 타입에 대해 같은 의미를 가질까?</p><p><code>Future&lt;String&gt;</code>의 뜻은 명확하다: 시간이 지난 뒤에 문자열 타입의 값이 발생한다는 뜻이다. 다른타입들에 이 의미를 확장할 수 있을까? 숫자, 객체, 배열? 그럴듯... 그럼 Future 자체에 대해서는어떨까? <code>Future&lt;Future&gt;</code>는 무슨 뜻일까? 그러니까 Future의 Future는?</p><p>보고 바로 이해할 수 있도록, 디렉토리를 보고 첫번째 파일의 내용을 읽는 간단한 예제를 만들어보자(간단히 생각하기 위해 내부에 다른 디렉토리가 없다고 가정한다).</p><p>Node.js에서는 비동기 함수 <code>fs.readdir</code>을 통해 디렉토리 속 파일들 이름의 배열을 가져올 수 있다.먼저 이걸 Future식 함수로 만들어보자.</p><figure class="highlight js"><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="comment">// readDirF: String -&gt; Future&lt; Array&lt;String&gt; &gt;</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">readDirF</span>(<span class="params">path</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> f = <span class="keyword">new</span> Future();</span><br><span class="line">  fs.readdir(path, <span class="function">(<span class="params">err, files</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="comment">// 기다리면 곧 실행된다</span></span><br><span class="line">    <span class="keyword">if</span>(err) <span class="keyword">throw</span> err;</span><br><span class="line">    f.complete(files);</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> f;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>readDirF</code>는 디렉토리 내 파일 이름들의 배열을 기다리는 Future를 뜻한다.</p><p>위에서 말한걸 구현하려면 필요한 나머지는</p><ol><li>Future가 잡고 있는 파일 이름들의 배열을 기다린다.</li><li>첫번째 파일명을 가져온다.</li></ol><p>여기서 <code>fmap</code>을 사용할 수 있을까? Node에서 이걸 실행해보자</p><figure class="highlight js"><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">var</span> resultF = readDirF(<span class="string">&quot;testdir&quot;</span>).fmap( <span class="function"><span class="params">files</span> =&gt;</span> readFileF( files[<span class="number">0</span>]) )</span><br><span class="line">logF( resultF )</span><br></pre></td></tr></table></figure><p>기다리면... 아차</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123; <span class="attr">slots</span>: [] &#125;</span><br></pre></td></tr></table></figure><p>확실히 뭔가 잘못됐다. 콘솔에서 파일 내용이 나오는게 아니라 Future 인스턴스 객체의 내용이 나왔다.</p><p>왜냐면 <code>fmap</code>은 매핑 함수의 결과가 무엇이든 받아서 그걸 Future로 잡아 돌려주기 때문이다. 위에서의매핑 함수는 또다른 Future(<code>readFileF</code>의 결과)를 <code>fmap</code>은 그 Future를 잡는 Future를 만들어<code>resultF</code>에 보내기만 한다.</p><p>하지만 Future는 잡고 있는 Future와 함께 끝나는지 않으므로, <em>속에 있는</em> Future가 완료될 때까지계속 기다릴 뿐이다.</p><p>그래서 이럴 때 필요한 함수를 만들어보자. Future를 리턴하고 끝내는 대신에 속에 있는 Future가 끝날때까지 기다리는 함수다.</p><p>(이중 Future)Future<Future>를 그냥 Future로 만들어주는 <code>flatten</code>를 만들어보자.</p><figure class="highlight js"><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="comment">// flatten: Future&lt; Future&lt;Value&gt; &gt; -&gt; Future&lt;Value&gt;</span></span><br><span class="line">Future.prototype.flatten = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line">  <span class="built_in">this</span>.ready(<span class="function"><span class="keyword">function</span>(<span class="params">fut2</span>) </span>&#123;</span><br><span class="line">    fut2.ready( <span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">      fut.complete(val);</span><br><span class="line">    &#125; );</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>이렇게 하면 원하는 결과를 얻을 수 있다.</p><figure class="highlight js"><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">var</span> result = readDirF(<span class="string">&quot;testdir&quot;</span>)</span><br><span class="line">              .fmap( <span class="function"><span class="params">files</span> =&gt;</span> readFileF(files[<span class="number">0</span>], &#123;<span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span>&#125;) )</span><br><span class="line">logF( result.flatten() )</span><br></pre></td></tr></table></figure><p><code>fmap</code>과 <code>flatten</code>을 따로 부르는 대신에 한번에 부를 수 있게 합쳐보자: 매핑 함수에서 나온 2중Future를 압축(flatten)하는 두가지 일을 한다. 하는 일 그대로 <code>flatMap</code>이라고 하자(좀 이상한건 나도 안다).</p><figure class="highlight js"><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">Future.prototype.flatMap = <span class="function"><span class="keyword">function</span>(<span class="params"> fn </span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">this</span>.fmap(fn).flatten();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>개념상으로는 위에서 독립적인 두 연산을 _이어서_하는 것인데, <code>readDirF</code>에서 나오는 파일 이름의배열을 <code>readFileF</code>에 넘겨준다.</p><p>여기에서 두번째 게스트가 등장하는데, Future를 Functor라고 부를 수 있는 것처럼 <strong>Monad</strong>라고 부를수도 있다. 순서대로 연산할 수 있는 방법에 대한 개념이다. 위에서 <code>flatMap</code>에서처럼, 이전 단계에서의결과를 다음 단계로 넘겨서 여러 함수를 연이어 연산할 수 있다.</p><p>Functor처럼 Monad도 많은 사용법이 있는데, 기술적으로 모든 모나드는 다음을 만족한다.</p><ul><li>일반값을 <em>Monad식(Monoadic) 값</em> 으로 lift하는 방법: 예를 들어, <code>Future.unit</code>은 일반값을 Future로 만든다.</li><li>연이은 연산 2개를 이어서 실행하는 방법: Monad는 연산을 이어서 실행하게 해주는 방법이 포함된다. 위에서 <code>flatMap</code>은 그냥 Future 하나만 만들고 다음으로 넘어가는게 아니라, 앞의 Future가 끝날 때까지 기다렸다가 넘어가는 방법이 들어있다.</li></ul><p>위에서 2개의 다른 연산(<code>fmap</code>과 <code>flatten</code>)으로 두번째 인터페이스(<code>flatMap</code>)를 만들 수 있다는것을 확인했다. fmap 함수를 정의하는 Functor라면 이중 구조를 단순화시켜서 합치는(flatten) 연산이필요해진다.</p><p>이제 처음의 질문으로 돌아가보자, Future들 여러개를 받는 함수를 어떻게 lift할 수 있을까?</p><p>다시 파일 예제로 돌아가서, 디렉토리의 모든 파일 내용을 합치려면 이런 코드가 될 것이다.</p><figure class="highlight js"><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">// concatF: (Future&lt;String, ...) -&gt; Future&lt;String&gt;</span></span><br><span class="line"><span class="keyword">var</span> resultF = concatF( text1F, text2F, ...)</span><br></pre></td></tr></table></figure><p>이건 무슨 뜻일까? 입력받은 Future들이 잡고 있는 각 문자열들을 합친 것을 다시 잡고 있는 Future를만들어준다. <code>concatF</code>는 입력받은 모든 Future들이 순서대로 처리되도록 기다려야하므로 결과로 나온Future는 입력받은 모든 Future가 완료될 때 완료된다.</p><p>인자 2개를 받는 경우부터 시작해보자.</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// fn: (Value, Value) -&gt; Value</span></span><br><span class="line"><span class="comment">// lift2: ( (Value, Value) -&gt; Value ) -&gt; ( (Future, Future) -&gt; Future )</span></span><br><span class="line">Future.lift2 = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function">(<span class="params">fut1, fut2</span>) =&gt;</span> &#123;</span><br><span class="line">    fut1.flatMap( <span class="function"><span class="params">value1</span> =&gt;</span> </span><br><span class="line">      fut2.flatMap( <span class="function"><span class="params">value2</span> =&gt;</span></span><br><span class="line">        Future.unit( fn(value1, value2) );</span><br><span class="line">      )</span><br><span class="line">    )</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>보이는 것과는 별개로 코드의 로직은 꽤 간단하다. 한줄씩 읽어보자면:</p><ul><li><code>Future.lift2</code>는 &quot;일반값 2개를 다루는 함수&quot;를 받아서 &quot;Future 2개를 다루는 함수&quot;를 리턴한다.</li><li>리턴된 (lift된) 함수가 실제로 하는 일은<ul><li>(중첩되어 실행되는) 2개의 연산을 순서대로 <code>flatMap</code>에 넣고</li><li>첫번째 연산은 그 자체로 하는게 없지만 <code>value1</code>을 바인딩해서 스코프에 묶어두는 역할을 하고</li><li>두번째로 중첩된 연산은 <code>value1</code>과 <code>value2</code>를 <code>fn</code>에 넘긴다.</li><li><code>fn</code>은 일반값을 리턴하는데 <code>flatMap</code>은 받은 함수가 Future를 리턴해야하므로 <code>Future.unit</code>을 통해 일반값을 Future로 lift한다.</li></ul></li></ul><p>이게 트릭이다: 모든 Future에서 순차적으로 <code>flatMap</code>을 실행해서 모두 끝나길 기다린 다음에 모든완료값이 한 스코프에 모였을 때 함수를 실행한다.</p><p><code>readDir</code> 내부에서 <code>readFile</code>를 실행하는 것처럼 순차 연산으로 설명되는 <em>Monadic</em> 값과는 다르게여러 인자를 한번에 lift하도록 마지막에 <code>Future.unit</code>를 사용했다.</p><p>파일 2개의 내용을 합치기 위한 예제다.</p><figure class="highlight js"><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">var</span> concat2 = Future.lift2( <span class="function">(<span class="params">str1, str2</span>) =&gt;</span> str1+<span class="string">&#x27; &#x27;</span>+str2 );</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> text1 = readFileF(<span class="string">&#x27;test1.txt&#x27;</span>, &#123;<span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span>&#125;);</span><br><span class="line"><span class="keyword">var</span> text2 = readFileF(<span class="string">&#x27;test2.txt&#x27;</span>, &#123;<span class="attr">encoding</span>: <span class="string">&#x27;utf8&#x27;</span>&#125;);</span><br><span class="line"></span><br><span class="line">logF( concat2(text1, text2) );</span><br></pre></td></tr></table></figure><p>두번째 Future <code>text2</code>가 첫번째의 <code>text1</code>보다 먼저 끝나더라도 <code>text1</code>을 기다리게 되고,<code>text1</code>이 끝나면 <code>text2</code>는 이미 끝났으므로 바로 함수를 실행한다.</p><p>여러 인자를 받는 함수는, 입력들이 언제 끝나는지나 의존성과는 관련없다는 것을 알 수 있다.이걸 정리하면 다음과 같다.</p><ul><li><code>fmap</code>이 하나의 연산을 실행하고</li><li><code>flatMap</code>은 순차 연산을 실행하지만</li><li>여러 인자를 lift하는 함수는 <em>병렬</em> 실행이다.</li></ul><p>위에서 봤듯이, Future들을 한번에 실행하고 연산이 진행되기 전에 이미 그 결과를 기다리고 있다.</p><p><code>lift2</code>의 패턴을 <code>lift3</code>이나 <code>lift4</code>로 쉽게 확장할 수 있지만, 인자의 갯수와 관계없이 위에서나온 중첩과 스코프를 통해 일반화를 구현해볼 것이다.</p><figure class="highlight js"><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><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">toArray</span>(<span class="params">args</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Array</span>.prototype.slice.call(args);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Future.lift = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> futArgs = toArray(<span class="built_in">arguments</span>), <span class="comment">// Future 인자들</span></span><br><span class="line">        ctx = <span class="built_in">this</span>; <span class="comment">// 컨텍스트(`this`)를 저장</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> bindArg(<span class="number">0</span>, []);</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">function</span> <span class="title">bindArg</span>(<span class="params">index, valArgs</span>) </span>&#123;</span><br><span class="line">      <span class="comment">// 현재 Future 인자를 기다린다</span></span><br><span class="line">      <span class="keyword">return</span> futArgs[index].flatMap(<span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">        valArgs = valArgs.concat(val); <span class="comment">// 완료값들을 모은다.</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> (idnex &lt; futArgs.length - <span class="number">1</span>) ? <span class="comment">// 아직 마지막 Future 인자가 아니라면</span></span><br><span class="line">          bindArg(index+<span class="number">1</span>, valArgs) : <span class="comment">// 다음 인자를 flatMap에 넘기고 기다린다</span></span><br><span class="line">          Future.unit( fn.apply(ctx, valArgs) ); <span class="comment">// 끝까지 오면 모은 완료값들을 함수에 넘긴다</span></span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>lift</code>에서는 <code>lift2</code>의 패턴을 재활용했다. 인자가 몇개 들어올지 정확히 모르니 재귀를 통해 전체를순회(iterate)하고, 완료를 기다렸다가 결과를 계속 넘겨서 모은다.(<code>index</code>번째의 Future를기다렸다가 완료값을 저장하고, 모든 입력이 완료될 때까지 다음 Future 입력에 이 연산을 반복한다.)마지막 Future까지 오면 함수를 실행하고 결과를 lift해서 리턴한다.</p><p>노트: 'Applicative Functor'라는 자료구조를 통해 n개 인자를 lift하도록 구현할 수 있지만,그러려면 람다나 커리에 대한 설명을 해야하므로 오늘은 일단 생략하자.</p><h2>에러 처리</h2><p>위에서 <code>fs.readFile</code>의 에러값을 어떻게 뒀는지 다시 확인해보자.</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">readFileF</span>(<span class="params">file, options</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> f = <span class="keyword">new</span> Future();</span><br><span class="line">  fs.readFile(file, options, <span class="function"><span class="keyword">function</span>(<span class="params">err, data</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(err) <span class="keyword">throw</span> err;</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure><p>실제로 작동하지 않는 코드다. 프로그램 흐름에서 떨어져서 실행중이므로 발생하는 에러를 잡을 방법이 없다.위의 상황에서 에러는 상위로 전파되며 잡는 핸들러가 없어서 Node.js 전체 프로그램을 중단시킨다.</p><p>에러를 잡아 흐름을 고치려고 한다거나 의미있는 메시지를 사용자에게 전달하는게 필요할 수도 있다.</p><p>가능한 방법으로는 <code>Future</code>에 <em>실패</em> 의 개념을 붙여서 의미를 확장하는 것이 있다. 아직까지는 Future의결과에 어떤 의미를 붙이지는 않았지만, 가능한 2가지 결과(완료 혹은 실패)로 Future를 생각해볼 수도 있다.실패에 대한 경우가 포함되었는지 확인해보자.</p><p>먼저, 완료를 알리는 <code>ready</code> 메소드가 있으니 실패를 알리는 메소드를 정의해보자.</p><figure class="highlight js"><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="function"><span class="keyword">function</span> <span class="title">Future</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">this</span>.slots = [];</span><br><span class="line">  <span class="built_in">this</span>.failslots = [];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Future.prototype.failed = <span class="function"><span class="keyword">function</span>(<span class="params">slot</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">this</span>.hasFailed) slot(<span class="built_in">this</span>.error);</span><br><span class="line">  <span class="keyword">else</span> <span class="built_in">this</span>.failslots.push(slot);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>Future가 실패할 때의 메소드도 정의해보자.</p><figure class="highlight js"><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">Future.prototype.fail = <span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">this</span>.completed || <span class="built_in">this</span>.hasFailed)</span><br><span class="line">    <span class="keyword">throw</span> <span class="string">&quot;이미 끝난 Future를 실패할 수는 없다!&quot;</span></span><br><span class="line">  <span class="built_in">this</span>.hasFailed = <span class="literal">true</span>;</span><br><span class="line">  <span class="built_in">this</span>.error = err;</span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>, len=<span class="built_in">this</span>.failslots.length ; i&lt;len ; i++) &#123;</span><br><span class="line">    <span class="built_in">this</span>.failslots[i](err);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>이제 <code>fmap</code>를 다시 생각해보자.</p><p><code>readFileF(...).fmap( s =&gt; s.length)</code> 예제에서 파일이 없을 때에 대한 처리가 없다.제대로 읽었을 때에 대해서만 변환하기 때문에 아닐 때는 에러와 함께 실패할 것이다. 혹시 변환 중 실패할경우에도 실패해야한다.</p><figure class="highlight js"><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">Future.prototype.fmap = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line">  <span class="built_in">this</span>.ready( <span class="function"><span class="params">val</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">try</span>        &#123; fut.complete( fn(val) ); &#125;</span><br><span class="line">    <span class="keyword">catch</span>(err) &#123; fut.fail(err); &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="built_in">this</span>.failed( <span class="function"><span class="params">err</span> =&gt;</span> fut.fail(err) );</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>flatten</code>은 약간 복잡하다. 안쪽과 바깥쪽의 Future 2개가 있고, 각각 완료될 수도 실패할 수도 있다.그래서 4가지(2x2) 경우를 다뤄야한다.</p><figure class="highlight js"><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></pre></td><td class="code"><pre><span class="line">Future.prototype.flatten = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 1- 밖깥 실패 안쪽 실패 =&gt; 결과 실패</span></span><br><span class="line">  <span class="comment">// 2- 바깥 실패 안쪽 완료 =&gt; 결과 실패</span></span><br><span class="line">  <span class="built_in">this</span>.failed( <span class="function"><span class="params">_</span> =&gt;</span> fut.fail(err) );</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 3- 바깥 완료 안쪽 실패 =&gt; 결과 실패</span></span><br><span class="line">  <span class="built_in">this</span>.ready( <span class="function"><span class="params">fut2</span> =&gt;</span></span><br><span class="line">    fut2.failed( <span class="function"><span class="params">err</span> =&gt;</span> fut.fail(err) );</span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 4- 바깥 완료 안쪽 완료 =&gt; 결과 완료</span></span><br><span class="line">  <span class="built_in">this</span>.ready( <span class="function"><span class="params">fut2</span> =&gt;</span></span><br><span class="line">    fut2.ready( <span class="function"><span class="params">val</span> =&gt;</span> fut.complete(val) );</span><br><span class="line">  );</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>flatten</code>에서 안쪽과 바깥쪽 모두 완료되었을 때만 결과가 완료된다.</p><p><code>flatMap</code>과 <code>lift</code>는 수정할 필요가 없다. 이미 <code>fmap</code>과 <code>flatten</code>의 의미를 가져오는 것이기때문에 자동으로 에러에 대한 의미가 추가된다.</p><p>자, 이제 실패한 Future들은 연산에서 제외하게 만들었다. 그럼 실패한 Future들을 어떻게 다뤄야할까?</p><p>Future 에러를 <em>잡아서</em> 고치면 된다. 어떻게? 실패한 Future를 완료값으로 변이시켜서 원래의 연산에포함시키면된다.</p><p><code>fmap</code>과 비슷하지만 좌우반전같은 <code>fmapError</code> 함수를 만들 것이다.</p><figure class="highlight js"><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">Future.prototype.fmapError = <span class="function"><span class="title">funciton</span>(<span class="params">fn</span>)</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line">  <span class="built_in">this</span>.ready( <span class="function"><span class="params">val</span> =&gt;</span> fut.complete(val) );</span><br><span class="line">  <span class="built_in">this</span>.failed( <span class="function"><span class="params">err</span> =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">try</span>         &#123; fut.complete( fn(err) ); &#125;</span><br><span class="line">    <span class="keyword">catch</span>(err1) &#123; fut.fail(err1); &#125;</span><br><span class="line">  &#125;);</span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>fmapError</code>는 <code>catch</code>문의 비동기 버전처럼 작동하며, 정상적으로 완료되면 그냥 값을 넘기고 에러가발생했을 때는 매핑 함수에 적용시켜서 완료값으로 넘긴다.</p><p>간단히 예제를 만들어보자</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">readFileF(<span class="string">&#x27;unknown file&#x27;</span>).fmapError( <span class="function"><span class="params">err</span> =&gt;</span> <span class="string">&#x27;alternate content&#x27;</span>)</span><br></pre></td></tr></table></figure><p>그럼 에러를 Monad식으로 파이프라인처럼 다음 연산으로 넘기려면?</p><p><code>flatMap</code>의 좌우반전같은 <code>flatMapError</code>를 만들어보자</p><figure class="highlight js"><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">Future.prototype.flatMapError = <span class="function"><span class="title">funciton</span>(<span class="params"> fn </span>)</span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">this</span>.fmapError(fn).flatten();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>예를 들어서 어떤 주소(URL)에서 내용을 가져오려고 할 때 요청이 실패한다면 다른 주소에서 가져오도록시도를 하려고 하는데, <code>flatMapError</code>을 사용해서 앞의 실패를 잡아서 다른 요청을 만들 수 있다.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">resultF = requestF(<span class="string">&#x27;/url1&#x27;</span>).flatMapError( <span class="function"><span class="params">err</span> =&gt;</span> requestF(<span class="string">&#x27;/url2&#x27;</span>) )</span><br></pre></td></tr></table></figure><p><code>resultF</code>는 첫번째 요청이 성공할 때 <code>'url1'</code>의 내용을 잡고 있고, 실패할 때는 <code>'url2'</code>를요청해서 그 결과를 잡고 있다는 뜻이다.</p><h2>부수효과</h2><p>합성해서 연산할 수 있는 방법에 대해 필요한 것들을 모두 다뤄보았다. 지금까지 다뤘던 함수들을 통해서Future를 동기 연산을 할 때처럼 일반값으로 넘겨서 비동기 처리를 하게 해봈다.</p><p>하지만 연산들은 끝까지 도달해야 결과가 나온다. 부수효과가 필요한 연산들을 다뤄 볼 시간이다. UI를업데이트한다거나 콘솔에 로그를 찍는다거나 데이터베이스에 저장을 한다거나.</p><p><code>ready</code>와 <code>failed</code> 이벤트를 사용할 수도 있지만 좋은 방법은 아니라고 생각한다.</p><p>실제 어플리케이션에서 한 Future가 여러 자식 Future들을 가지고 그 Future들은 또 자식 Future들을갖게 되는 트리같은 구조가 된다. Future하나가 완료돌 때 매핑된 Future들이 연쇄적으로 완료된다.</p><p>Future의 <code>ready</code>알림을 통해서 부수효과를 실행하려고 한다면 트리 내부에 있는 Future들 전체에영향을 끼치게 된다. 의미적으로나 구현상으로나 업데이트가 끝날 때까지 부수효과 연산을 미뤄두는 것이 좋다.예를 들어 DOM을 업데이트할 때는 <code>requestAnimationFrame</code>같은 스케쥴러에 맡기는게 더 좋을 수도 있다.</p><p>위에서 말한 이유로 <code>do</code>라는 메소드를 하나 만들텐데 부수효과 연산을 명시하는 것이다. <code>fmap</code>처럼부수효과 함수를 받겠지만, 내부의 알림들(<code>ready</code>와 <code>failed</code>)이 완료될 때까지 지연될 것이다.</p><p>예를 들어</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">requestF(<span class="string">&#x27;/url&#x27;</span>).do( <span class="function"><span class="params">val</span> =&gt;</span> <span class="comment">/* update ... */</span> )</span><br></pre></td></tr></table></figure><p>이번에도 <em><code>do</code>의 의미와 리턴값이 무엇인지</em> 생각해보자.</p><p>변이없이 그냥 Future를 리턴한다면 <code>future.fmap( Id )</code>(여기에서 <code>Id</code>는 <code>x =&gt; x</code> 같은 항등함수)와 같은 형태이다. <code>fmap</code>과 다른 점은, 먼저 <code>do</code>에서 부수효과가 발생한다는 점이고 두번째는 다른컨텍스트에서 실행된다는 점이다.(<code>fmap</code>은 즉시, <code>do</code>는 나중에). 가장 다른건 _의미_다.</p><blockquote><p>정정: 2015년 4월 6일. <code>Action</code>이라는 새로운 타입을 통해 <code>do</code>를 적용했는데, 굳이 Monad(Future)안에 다른 Monad(Action)을 넣어 복잡하게 만들 필요가 없었다. 서버에 데이터를 넘기거나 응답을기다리는 등의 상황에서 리턴값이 필요할 수도 있는데, 다음 글에 이걸 개발해 볼 수도 있다.</p></blockquote><p>빠르게 대충 구현해보자.</p><figure class="highlight js"><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><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">Future.prototype.do = <span class="function"><span class="title">funciton</span>(<span class="params">action</span>)</span> &#123;</span><br><span class="line">  <span class="keyword">var</span> fut = <span class="keyword">new</span> Future();</span><br><span class="line">  <span class="keyword">if</span>(<span class="built_in">this</span>.completed) &#123;</span><br><span class="line">    action(<span class="built_in">this</span>.value);</span><br><span class="line">    fut.complete(<span class="built_in">this</span>.value);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.actions.push( <span class="function"><span class="params">val</span> =&gt;</span> &#123; </span><br><span class="line">      action(val);</span><br><span class="line">      fut.complete(val);</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> fut;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Future.prototype.complete = <span class="function"><span class="keyword">function</span>(<span class="params">val</span>) </span>&#123;</span><br><span class="line">  ...</span><br><span class="line">  <span class="keyword">var</span> me = <span class="built_in">this</span>;</span><br><span class="line">  <span class="built_in">setTimeout</span>( <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">var</span> i=<span class="number">0</span>, len=me.actions.length ; i&lt;len; i++)</span><br><span class="line">      me.actions[i](val);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>덧붙여서, 비동기실행을 제대로 구현하려면 process.nextTick이나 MessageChannel 등을 사용해야하지만 여기서는 간단히 구현하고 넘어가자. 비슷하게, 부수효과의 실패에 대응해 <code>doError</code>도 만들어야하는데, <code>do</code>와 비슷하므로 각자 알아서 구현해보자. (<a href="https://gist.github.com/yelouafi/40aeb2a70a368acb6e45">Gist에 코드 전체가 있다</a>)</p><hr><p>역주 1: Promise와의 비교는 Future/Functor/Monad 개념을 이해하는데 관계없다고 생각해서 생략했다.</p><p>역주 2: <a href="http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html">그림으로 설명하는 Functor, Applicative, Monad</a>(<a href="http://netpyoung.github.io/external/functors_applicatives_and_monads_in_pictures/">번역</a>)과 함께 읽으면 이해하는데 도움이 될 것이다.</p>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/translation/">translation</category>
      
      <category domain="http://seoh.blog/tags/monad/">monad</category>
      
      <category domain="http://seoh.blog/tags/functor/">functor</category>
      
      
      <comments>http://seoh.blog/2015/05/28/callback-to-future-functor-applicative-monad/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>번역, 스칼라로 전환</title>
      <link>http://seoh.blog/2015/01/18/transitioning-to-scala/</link>
      <guid>http://seoh.blog/2015/01/18/transitioning-to-scala/</guid>
      <pubDate>Sun, 18 Jan 2015 13:21:37 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;h3&gt;Translation of &amp;quot;&lt;a href=&quot;https://medium.com/p/d1818f25b2b7&quot;&gt;Transitioning to Scala&lt;/a&gt;&amp;quot; into Korean, under the sa</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><h3>Translation of &quot;<a href="https://medium.com/p/d1818f25b2b7">Transitioning to Scala</a>&quot; into Korean, under the same license as the original.</h3></blockquote><p>2011년 말부터 2014년 초까지 전자상거래 솔루션 전문 에이전시, <a href="https://www.nurun.com/en/careers/toronto/">Nurun Toronto</a>에서 리드개발자로 일했다. 자바와 스프링만으로 새로운 프로젝트들을 계속하다보니 대체제를 찾아야할 때라는걸 알게 되었다.</p><p>에이전시에서의 업무를 장기적인 관점에서 생각해봤다. &quot;자바가 구리다&quot;를 이해하지 못하는 고객들 때문에 마감에 시달렸다. 2004년도에 어플리케이션을 만들던 툴과 테크닉들은 2014년에는 별 도움이 되질 않았다. 2004년에는 코드 한줄을 테스트하기 위해 서버를 재시작하는게 당연했다 - 웹스피어는 재시작할 때 정체불명의 1200줄 XML 설정파일을 읽어오는데, 평균 걸리는 시간이 커피 한잔 마시고 오기 딱 좋은 120초 정도다. 요새는 이렇게 하면 에이전시나 개발자나 망한다.</p><p>우리가 만드는 어플리케이션들은 단순히 데이터베이스의 뷰어가 아니라, 매일매일 똥을 치워주는 소중한 도구다.</p><h3>1. 왜 타입세이프의 스택을?</h3><p>지난 프로젝트를 루비온레일즈로 진행해보고 뭘 싫은지를 알았다. 다른 동적 타입 언어도 인터프리트 언어도 상태기반 웹 프레임워크(stateful web framework)도 쓰기 싫었다. 자바 바이트코드로 컴파일되는 정적 타입 언어나 활발한 생태계를 가진 툴들, 그리고 확장성을 위한 무상태 웹 프레임워크가 더 괜찮았다. 또한 우리 고객사들은 믿을만한 회사를 통해 전문적인 기술지원을 받을 수 있어야 마음의 위안을 가졌다.</p><p>그래서 몇가지 선택지를 꼽아봤는데, 가장 먼저 타입세이프가 떠올랐다. 우리가 원하는 모든 것들이 있었다.</p><p>컨셉증명을 위해 초소형 전자상거래 사이트를 내부적으로 만들어서 스칼라의 단순함과 플레이의 개발 생산성에 대해서 시연했고 충분히 관심을 끌어서 결국 <a href="http://walmart.ca/">월마트 캐나다</a>의 새로운 전자상거래 플랫폼의 토대가 되었다.</p><h3>왜 스칼라가 복잡하다고 느낄까?</h3><p>스칼라는 <em>유연하다</em>. 유연하다는 것은 단순하다는 것을 포기해야하는 일이지만, 다른 면으로 스칼라는 단순히 &quot;자바보다 나은&quot; 정도가 아니라 그보다 더 좋으며 매우 우아한 언어이다. 스칼라나 새로운 어떤 언어로의 전환이라는 큰 도전은 단지 기술적인 일만이 아니다. 능력있는 개발자라면 새로운 문법, 새로운 개념, 새로운 IDE를 배울 수 있다. 변화는 기술보다는 그 과정이나 문화같은 다른 면에서 어렵다.</p><blockquote><p>짧게 말해서, 모든 것은 사람에 달려있다.</p></blockquote><p>이 글의 뒷부분은 스칼라 프로그래밍 튜토리얼이 아니다. 이미 많은 글들이 있고, 고급 스칼라의 깊은 부분에 대한 최신 트릭을 가르칠 만큼 나는 인정받은 스칼라 개발자도 아니다. 이 뒤로는 스칼라로의 전환을 생각하고 있는 개발자들, 팀장 혹은 매니저들에게 전하는 조언들이다. 이 조언들은 기업용 스칼라 프로젝트를 이끌 때 개인적으로 한 경험을 토대로한 것들이다.</p><h3>스칼라로의 전환을 생각 중인 매니저와 개발자들에게 하는 조언</h3><figure><img src="/images/transitioning-to-scala/1.jpg" /><figcaption class="image-caption">스칼라, 커피 한잔보다 좋다!</figcaption></figure><h3>1. 언어의 기능들을 이해해라</h3><p>모든 스칼라 개발자, 팀장, 매니저는 마틴 오더스키의 <a href="http://www.scala-lang.org/old/node/8610">스칼라 레벨 가이드</a>를 읽어야한다.</p><p>전업 스칼라 개발자로 경력 1년반이 지나고 엔터프라이즈 스칼라 프로젝트도 진행했지만, 마틴의 가이드에서 스칼라 개발자 등급 A2.5/L1.5라고 생각한다. A3/L3에 있는 테크닉들을 사용하지만, 웹 어플리케이션을 쭉 개발해오면서 대부분은 써본 적이 없다. 케이크 패턴을 써본 적도 없고, 고계도 타입(high-kinded type, 역주: <a href="http://twitter.github.io/scala_school/ko/advanced-types.html#higher">스칼라 학교</a>에서는 <em>상류 타입</em>이라고 번역했는데, 하나의 Layer 위에 있다는 생각으로 고계高階를 생각해봤다)을 써본 적이 아직 없다. 그렇다고 나쁜 개발자도 아니고, <a href="http://ko.wikipedia.org/wiki/%EA%B0%80%EB%A9%B4%ED%98%84%EC%83%81">가면현상</a>의 증상도 아니고, 단지 내 시간은 한정되어있고 가장 돈이 되는 것에 집중하려고 한다. 게다가 드럼도 치고 기타도 치고 일주일에 두번 댄스 레슨도 다니고 커피도 많이 마시고 데이트하러 나가야한다. 시간은 소중하니까.</p><p>Walmart.ca 프로젝트에서는 콤비네이터 파서와 폴드를 사용하지도 않고 레벨 가이드의 얇은 부분만을 썼다. &quot;얇은&quot; 스칼라로도 이전 플랫폼보다 훨씬 좋은 생산성을 보여줬다. 구현하는데 골치아픈 일도 없었다. 그렇게 짠 코드들은 이전보다 더 관리하기에도 좋고 생산성도 더 좋았다. 블랙 프라이데이나 박싱 데이(역주, 북미지역 등지에서 추수감사절 시즌/크리스마스 시즌에 대부분의 쇼핑몰들이 매년하는 대량할인 이벤트 기간들)에서의 확장도 완벽하게 돌아갔고, 많은 자바기반의 전자상거래 플랫폼은 하지 못했던 것들이다.</p><p>그래서 중요하지 않다는건가?</p><p>단순하게 스칼라를 쓴다는 것을 스칼라가 부족하다는 것으로 착각하지 마시길. A1과 A2 등급에서 익힐 수 있는 것들을 보자.</p><ul><li>간단한 클로저</li><li>map, filter 등의 콜렉션</li><li>패턴 매칭</li><li>trait 합성</li><li>재귀</li><li>XML 표현식</li></ul><p>(역주: 코멘트에서 XML 표현식은 <a href="http://www.swift.com/">SWIFT</a>에서 사용중이라고 한다.)</p><p>자바에 몇개 더 추가한 것과 비슷하다. 서술하듯이 개발하고 소프트웨어를 관리하는 새로운 방법이다. A3에 있는 몇개도 익히기 꽤 쉽고 - Akka나 다른 병렬 처리 라이브러리들을 사용하기 위해 꽤나 중요한 것들인데 - 그에 비해 크게 어렵지 않고 수학 학위가 필요할 정도는 아니다.</p><ul><li>fold</li><li>stream, 혹은 지연평가 자료구조</li><li>actor</li><li>combinator parser</li></ul><p>이런 테크닉들을 익혔을 때의 좋은 부가효과는, 사용하는 <strong>모든</strong> 언어에서 더 좋은 개발자가 된다는 것이다. 나는 스칼라와 자바스크립트 모두에서 클로저나 다른 테크닉들을 익히는데 정말 도움이 됐고 더 좋은 자바스크립트 프로그래머가 되었다.</p><h3>2. 시간을 써라</h3><p>자바에서 건너온 많은 스칼라 개발자들은 바로 적응하고 싶어하지만 스칼라는 완전히 다른 언어다. 새로운걸 익힌다는 것은 연습을 필요로 한다. 스칼라도 예외는 아니다.</p><p>좋은 소식은 A2/L1 등급만으로도 충분히 스칼라 어플리케이션을 만들만한 자격이 있다는 것이다. 모든 스칼라 개발자가 고급 순수함수 자료구조, 타입 이론, 고계도 타입을 이해하고 있을 필요는 없다. 하지만 스칼라는 차세대 어플리케이션을 만드는 전문 개발자를 위한 프로그래밍언어라는 것이다. 그래서 배우고 체득하는데 많은 시간이 걸릴 것이다.</p><h3>3. 배우는걸 두려워말라</h3><p>자바 개발자라면 다음과 같은 자료들을 통해 스칼라를 배우길 강력히 추천한다.</p><ul><li><a href="http://www.amazon.com/Scala-Impatient-Cay-S-Horstmann/dp/0321774094">Scala for the Impatient</a>를 읽어라. 특히 <em>예제</em>가 필요한 성격급한 개발자들에게 좋은 시작점이다. (역주, 번역판 <a href="http://www.bjpublic.co.kr/skin12/product_list.php?boardT=v&amp;page_idx=9&amp;goods_data=aWR4PTk2JnN0YXJ0UGFnZT0zNiZsaXN0Tm89NjEmdGFibGU9cmVkX2dvb2RzJnBhZ2VfaWR4PTkmc2VhcmNoX2l0ZW09%7C%7C">쉽게 배워서 빨리 써먹는 스칼라 프로그래밍</a>, 2013 비제이퍼블릭)</li><li>마틴 오더스키, 렉스 스푼, 빌 베너스의 <a href="http://www.amazon.com/Programming-Scala-Comprehensive-Step---Step/dp/0981531644">Programming in Scala</a>를 읽어라. <em>Scala for the Impatient</em>보다 더 자세한 책이라 언어의 기능들에 대해 폭넓은 시야를 익히기에도 좋다. (역주, 번역판 <a href="http://www.acornpub.co.kr/book/programming-in-scala">Programming in Scala</a>, 2014 에이콘)</li><li>가능하면 Coursera의 <a href="https://www.coursera.org/course/progfun">Functional Programming Principles in Scala</a> 코스를 들어라. Coursera가 Scala로 만들어졌다.</li><li><a href="https://typesafe.com/activator">Typesafe Activator</a> 템플릿들을 살펴봐라. 다른 언어들에 비해 온라인 문서나 학습자료가 부족하기도 하지만, 다른 사람 코드를 분석하는게 제일 좋은 방법이기도 하다. 특히 James Ward같은 능력있는 개발자들이 짠 코드라면.</li><li>가능하면 Coursera의 <a href="https://www.coursera.org/course/reactive">Principles of Reactive Programming</a> 코스를 들어라. 스칼라와 대량 데이터 처리를 위한 Akka를 사용하려는 개발자들에게 좋은 자료다.</li><li>스칼라 모임에 참석하고 스칼라를 사용하는 실무자들이 어디에 쓰는지 배워라.</li><li><a href="http://www.amazon.com/Functional-Programming-Patterns-Scala-Clojure/dp/1937785475">Functional Programming Patterns in Scala and Clojure</a>를 읽어라. 예전의 명령형 스타일 코드와 더 읽을만해진 함수형 스타일 코드를 비교해보고 더 함수형의 언어를 배우고 싶어졌다. 하스켈은 좀 과하고, 그래서 Clojure를 배우기 시작했다. 그리고나서 내 스칼라 코드는 더 간략해지고 더 의미있어졌다.</li></ul><h3>4. 온라인에서 읽는 것들은 적당히 감안해서</h3><p><a href="https://github.com/scalaz/scalaz">Scalaz</a>를 만든 <a href="http://blog.tmorris.net/">토니 모리스</a>처럼 다른 세계에서 온 똑똑한 개발자들이 많이 있다. 하스켈 세계나, 함수형 프로그래밍, 그리고 수학 분야.</p><p>토니는 다음과 같은 함수 선언에 반대를 한다.</p><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">def reverse[<span class="keyword">A</span>]: List[<span class="keyword">A</span>] =&gt; List[<span class="keyword">A</span>]</span><br></pre></td></tr></table></figure><p>그리고 이런 선언을 더 선호한다.</p><figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">def &lt;-:[<span class="keyword">A</span>, B](f: <span class="keyword">A</span> =&gt; B): List[<span class="keyword">A</span>] =&gt; List[B]</span><br></pre></td></tr></table></figure><blockquote><p>다음에 &lt;-:라는 이름의 함수를 보면 이렇게 생각해라, &quot;으악, 읽기도 구리고 내가 뭘 하고 있는거야?&quot;, (아마 친근보다는 좀 강력한) 다른 툴은 없는지? 이거 타입은 뭐지? 대수적인 속성은 뭐지? 드러난 속성들이 또 뭐가 있지?</p><p><a href="http://blog.tmorris.net/posts/identifier-names/index.html">Sticks, stones, but names are not useful to me</a> by Tony Morris</p></blockquote><p>이게 스칼라의 미학이다. 토니도 맞다. 틀린건 없고, 그냥 개인과 팀의 선택에 대한 문제다. 나는 &lt;-:보다 <strong>reverse</strong>를 더 선호하지만, 내가 가독성과 단순성을 선호하는만큼 토니같은 개발자들은 수학적인 순수성과 사실성을 선호한다. 이 스타일들은 항상 다른 것 같다. 토니는 라이브러리들을 개발했고, 나는 라이브러리들로 어플리케이션을 만들고, 우리 둘 다 스칼라로 개발한다. 나는 가끔 <em>var</em>를 쓰고 나중에 걷어내지만, 누군가는 그걸 질색한다.</p><p>그런데 팀에서 사람들이 영어, 불어, 독어 같이 한 언어가 아닌 여러 언어를 쓴다고 해보자. 이럴 때 함수 이름을 영어 동사로 써도 그러려니 한다. 내가 같이 일했던 캐나다 사람들은 영어와 불어를 쓰는 사람들이 많았고, 헝크러진 머리나 좁쌀만한 눈(역주, 미국인이 캐나다인을 놀릴 때 주로 쓰는 표현)보다는 보기 괜찮다고 확신할 수 있다. 젠장(<em>Sacré bleu!</em>)</p><p>스칼라처럼 독선적이지 않은 언어는 그래서 아름답다. 자기 스타일을 자유롭게 적용할 수 있고, 언어가 그걸 방해하지 않는다. &lt;-:도 <em>쓸 수 있다는</em>게 마음에 든다.</p><h3>5. 주머니가 허락한다면 기술지원을 받아라</h3><p>나는 운좋게도 타입세이프의 Nilanjan Raychaudhuri와 Roland Kuhn같은 진짜 고수들에게 배워서 Nurun에 설계 리뷰, 코드 리뷰, 페어 프로그래밍을 도입할 수 있었다. 새로운 프로그래밍 스타일을 배운 덕분에 신뢰도를 월등히 높인 프로젝트를 진행하면서 다방면으로 값으로 따질 수 없는 도움을 받았다.</p><p>단순히 스칼라의 함수형 스타일뿐만 배운게 아니라, 리액티브 프로그래밍 컨셉도 배웠다. Play와 Akka도. 새로운 테크닉들과 프로젝트 전반에 걸쳐 타입세이프의 도움을 많이 받았다. 우리가 항상 제대로 된 길을 가고 있는지에 대해 확신을 받았다.</p><p>타입세이프의 이메일 지원 역시 훌륭하다. <a href="http://typesafe.com/how/subscription">지원 구독</a>은 주머니가 허락한다면 지출할 가치가 충분히 있다.</p><h3>6. 다양성을 포용하라</h3><p>스칼라 커뮤니티에는 전세계의 다양한 프로그래머들이 모여있다. 나처럼 전직 자바 개발자도 있고, 학계에서 온 사람들도 있다. 독학한 개발자도 있고 박사학위자들도 있다. 빠듯한 예산으로 사업문제를 해결하려 하는 사람들도 있고, 관심분야를 넓히려고 하는 사람들도 있다. 어플리케이션을 만드는 사람도 있고, 라이브러리를 만드는 사람도 있다.</p><p>커뮤니티의 모든 사람을 존중하고 이해하는게 좋은 개발자가 되는데 중요하다. StackOverflow에 단순한 질문을 올렸는데, 이해하려면 카테고리 이론을 몇년 배워야하는 난해한 답변이 달릴지도 모른다. 하지만 스칼라는 아직 새로운 언어이고 커뮤니티는 자기 색깔을 찾아가고 있다는 것을 염두에 둬라. 학계 출신이 아닌 개발자들이 스칼라 세계에 더 많아지고 더 많이 답변하다보면 토론은 조금 더 <em>이론</em> 개념보다는 <em>어플리케이션</em> 개념으로 옮겨갈 것이다.</p><p>답변에 실망했다면, 트위터의 <a href="https://github.com/twitter/finagle">Finagle</a>이나 mDialog의 <a href="https://github.com/mDialog/smoke">Smoke</a>같은 라이브러리들의 소스코드를 봐라. 두 프로젝트는 제품 레벨에서도 많이 쓰이는 구현체로 훌륭한 스칼라 예제이다. 모든 스칼라가 어마어마하게 복잡하지는 않다.</p><h3>7. 현실적인 목표를 설정하라</h3><p>자바에서 온 신입 스칼라 개발자들은 하룻밤만에 고급 스칼라, 함수형 스칼라를 배울 수 없다는 사실을 깨달아야한다. 전형적인 비지니스 어플리케이션을 성공적으로 개발하는데 고급 함수형 스칼라가 필요한건 아니다.</p><p>함수형 프로그래밍을 접해본 개발자라면 스칼라 스타일로 익히데 시간이 덜 걸릴 것이다. 그렇지만 팀원들 대부분이 명령형 언어 개발자들이라면 스타일을 맞춰야할 것이다.</p><p>그리고 팀 밸런스에 대한 것인데, 유지보수해야할 사람이 이해할 수 있는 코드를 짜야할 것이다. 아무리 전세계에서 가장 우아한 코드라도 관리할 수 없으면 쓸모없다.</p><h3>8. 짝코딩과 코드리뷰는 의무</h3><p>짝코딩을 하면 팀 전체 스타일과 기술 평균에서 너무 멀어지지 않게 해주는데 효과적이다. 마지막으로 바라는게 필멸자들이 감히 범접할 수 없는 어려운 코드를 짜거나, 팀원들이 다들 준비가 될 때까지 다시 완벽하게 작동하는 명령형 코드로 짜는 것이다. 실험은 중요하지만, git은 두었다 무엇하는가. 포크해라, 두번해라.</p><figure><img src="/images/transitioning-to-scala/2.png" /><figcaption class="image-caption">팀원들이 이렇게 된다</figcaption></figure><p>스칼라의 유연성 덕분에 복잡함의 칼날을 피하는 것도, 언어의 새 기능이나 스칼라의 표현력을 익히기 쉽다. 문화는 언제나 개발팀에게 중요하지만, 더 중요한건 스칼라를 처음 배울 때 다같이 페달을 밟아 나가야한다는 것이다.</p><h3>9. 간결함을 유지하라</h3><p>스칼라는 새로운 것이고, 사람들은 무엇을 써야하고 무엇을 피해야할지 여전히 배우는 중이다. 더글라스 크록포드가 스칼라를 마스터하고 <em>Scala: The Good Parts</em>를 쓰기 전까지는, 언어의 각 부분에 대한 가치를 알아서 확인해야한다. 옳고 그름은 없고, 단지 시도와 실패만 있을 뿐이다. 뭐가 더 맞는지에 대해 얼마든 질문해라.</p><p>Reflection API가 처음 자바에 도입되었을 때, 모든 자바 개발자들이 자신들의 지적 능력을 동원해 <strong>모든 것</strong>에 리플렉션을 사용하려고 했다. 당시 내가 개발하던 코드들은 관리하기가 더럽게 복잡해졌고, 한 개발자가 미쳐날뛰어서 이해하지 못하는 기능을 남용했다는 것 말고 다른 이유는 없었다. 모든 생소한 기능은 발을 담그기 전에 천천히 깨끗하고 심플한 코드를 짜는게 더 낫다. 고급 테크닉을 이상하게 뒤죽박죽으로 구현한 것보다 깔끔하게 명령형 스타일로 스칼라 코딩을 하는게 차라리 낫다.</p><figure><img src="/images/transitioning-to-scala/3.jpg" /><figcaption class="image-caption">준비가 되기 전에 깊게 들어가지마라. 천천히 가자.</figcaption></figure><p>좋은 음악처럼 좋은 코드도 우아하고 드물다. 좋은 음식에 꼭 좋은 재료가 들어가는건 아니다. 상상할 수 있는 모든 향신료가 들어간 음식을 먹고 싶어할 사람이 있을까? 코드를 쓰는 것도 그렇다. A1급 개발자가 쓴 신뢰할만한 코드는 자기가 뭘 하고 있는지 왜 하는지도 모르며 제멋대로 짠 A3/L3급 개발자의 코드보다 더 관리하기 쉽다.</p><h3>10. 구린 코드를 살펴봐라</h3><p>심각하게 구린 스칼라 코드를 짤 수도 있고, 자바, 펄, 그리고 영어도 마찬가지다.</p><p>하지만 구린 자바코드와 구린 스칼라코드의 중요한 차이가 있다.</p><p>구린 스칼라 코드는 좀 다른 방식으로 구리다. 명령형으로 구리거나, 함수형으로 구리거나, 혹은 두가지가 섞인 채로 구리거나. 이해할 수 없을 정도로 구리다면 익숙하지 않은 스타일이라서 그럴 수도 있다. 스칼라는 새로운 언어라, 자바같은 성숙된 언어처럼 바로 안티패턴을 발견해내는게 아직은 어렵다. 그래서 개발팀들이 아름다운 코드를 구리다고 착각할 수도 있고, 구린 코드를 아름답다고 착각하게 될 수도 있다. 개발자들은 배운대로 구린 패턴을 짜기 시작하면 나중에는 더 구린 코드가 나온다. 그렇게 악순환이 된다.</p><p>나중에 고치려고 하는 것보다 처음부터 피하는게 더 좋다.</p><p>똑똑한 개발자가 스칼라로 이상하게 코딩한다면 불러봐라. 질문해라. 익히지 못한 언어에 대해 추측하지마라. 최악의 경우는, 잘못짰으면서 우아한 코드라고 생각은 하는데, <em>왜</em> 우아한지 이해하지 못하는 경우다.</p><h3>11. 스칼라는 단지 퍼즐의 일부분</h3><p>웹 어플리케이션을 개발하는 방법은 10년전에 비하면 매우 다양하다. 요새는 스칼라, 플레이, AngularJS, MongoDB 앱을 개발한다. 내가 짜는 코드 대부분은 클라이언트단이다. 몇년간은 스칼라보다 Angular를 더 많이 짰는데, 나쁘다는게 아니라 그냥 현실이 그렇다.</p><p>스칼라의 미학은 자바처럼 쓸데없는 밑바닥을 만들어야하거나 루비같은 동적 언어의 불안함을 걱정할 필요없이 깔끔하고 안정적이고 성능좋은 서버단 코드를 짤 수 있게 해준다는 것이다. 스칼라로 짠 서버쪽 로직은 견고하기에 클라이언트쪽 코드를 안정적으로 짜는데 시간을 투자할 수 있다.</p><p>스칼라의 모든 쪽에서 마스터가 되고 싶어하는만큼 파고들어야할 기술들이 너무 많다. HTML5, SASS, AngularJS, RequireJS, SQL, MongoDB, 또, 또, 또.</p><p>한 언어의 모든 면을 마스터할 시간은 없겠지만, 스칼라는 맛을 보기만 해도 괜찮은 기술이다. <a href="http://www.reactivemanifesto.org/">Reactive Programming</a>은 다음 세대의 대세가 될 것이며 그 패러다임 전환의 선두에 스칼라가 있을 것이라 믿는다. 성능과 안정성을 모두 얻을 수 있는 리엑티브 어플리케이션을 무시하기는 힘들다.</p><p>요즘엔 대부분 묵직한 XML 대신 JSON을 쓴다. SOA 패턴 대신 REST를 쓴다. 데스크탑 대신 모바일을 쓴다. 어마어마한 크기의 데이터에서 필요한 정보를 뽑아낼 때, Akka의 성능이라면 막대한 하드웨어를 투자하지 않고서도 가능하다.</p><p>스칼라는 퍼즐의 한 부분일 뿐이지만, 새로운 종류의 개발을 위해 필요한 다른 많은 부분들의 심장과도 같다.</p><h3>12. 스칼라를 배우면 더 좋은 프로그래머가 된다</h3><p>직장인 개발자가 자기 영역을 넓히는건 <em>정말</em> 드문 일이다. 소프트웨어 개발에서 완전히 다른 접근법을 배워본게 마지막으로 언제인가?</p><p>마지막 전환(그리고 내 경력에서 겪었던 유일한 전환)은 절차지향 언어에서 객체지향 언어로의 변환이었다. CIBC에서 인턴하던 1998년에 운좋게도 첫 자바 어플리케이션 개발자 중 하나가 될 수 있었다. 대부분의 개발자들은 전직 COBOL이나 C였다가 전환하는 시점이었다. 요새는 뭐든 다 자바를 쓰지만, 당시에 윈도우와 OS/2에 모두 배포해야하는 상황에서 자바는 매우 실용적이었다.</p><p>2-30년 경력의 개발자들(몇명은 실제로 펀치카드로 프로그래밍을 해봤던)과 일하면서 좋은 경험을 쌓았고, 한가지 스타일에 매이면 안된다는 것을 깨달았다. 자바를 배울 때 JCL도 관리해야했다. 바로 다시 복귀하기 몇달전까지는 old COBOL과 360 어셈블리도 파고들었다. 넓게 보자. JCL과 COBOL이 섹시한 언어는 아니지만 필요한 분야에서는 괜찮은 언어다. 인턴시절 스몰토크에서 엑셀까지 모든 것을 겪어볼 수 있었다.  엑셀은 처음 접한 함수형 프로그래밍이다. (역주: 엑셀이 함수형 프로그래밍을 지원하는지에 대한 <a href="http://programmers.stackexchange.com/questions/125990/questions-over-excel-programming">StackExchange</a>의 글이 댓글에 있다.)</p><p>스칼라는 부당한 평을 많이 받았다. 어떤 개발자들은 생각을 깊게 하기보다는 잠깐 시도해보고 익숙한 언어로 도망친다. 문제는 그 사람들이 인터넷에 남긴 불평들 때문에 관심있어하는 개발자들에게 언어의 가치가 잘못 전달될 수도 있다.</p><p>스칼라에 대한 불만을 읽는다면 누가 썼는지 찾아보기를. 다른걸 원한 사람일 수도 있다. 쉽게 흔들리지 말고, 불평하는 사람들에게 얽매이지 마라. 점점 널리 퍼져가는 스칼라의 성공사례들을 찾아보기를 바란다.</p><h3>결론</h3><p>스칼라는 기술뿐만이 아니라 문화적인 투자다. 투자할만한 가치가 있는 보상이 있는데, 어쨋든 해봐야하지 않을까? 확장가능하고 믿을만하고 관리하기 편한 프로젝트를 진행중이라면, 혹은 프로그래머로서 사고를 확장하고 싶다면 단언컨데 스칼라는 할만 하다. 기본만 있으면 보이는 것만큼 어렵지도 않다.</p><p>스칼라는 실무에서도 쓸만하다는걸 기억해라, 아니 이미 쓰이고 있다.</p><style type="text/css">.image-caption {position: relative;width: 100%;text-align: center;left: 0;margin-top: 10px;letter-spacing: .01rem;font-weight: 400;font-style: italic;font-size: 14px;line-height: 1.4;color: #666665;outline: 0;z-index: 300;}</style>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/scala/">scala</category>
      
      <category domain="http://seoh.blog/tags/translation/">translation</category>
      
      
      <comments>http://seoh.blog/2015/01/18/transitioning-to-scala/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>책 후기, Programming in Scala 2nd</title>
      <link>http://seoh.blog/2015/01/18/pis-review/</link>
      <guid>http://seoh.blog/2015/01/18/pis-review/</guid>
      <pubDate>Sat, 17 Jan 2015 20:31:17 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;p&gt;이런 테크닉들을 익혔을 때의 좋은 부가효과는, 사용하는 &lt;strong&gt;모든&lt;/strong&gt; 언어에서 더 좋은 개발자가 된다는 것이다. 나는 스칼라와 자바스크립트 모두에서 클로저나 다른 테크닉들을 익히는데 정말 도움이 됐고 </description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><p>이런 테크닉들을 익혔을 때의 좋은 부가효과는, 사용하는 <strong>모든</strong> 언어에서 더 좋은 개발자가 된다는 것이다. 나는 스칼라와 자바스크립트 모두에서 클로저나 다른 테크닉들을 익히는데 정말 도움이 됐고 더 좋은 자바스크립트 프로그래머가 되었다.</p><p><a href="https://medium.com/@kvnwbbr/transitioning-to-scala-d1818f25b2b7" title="Transitioning to Scala">스칼라로의 전환</a>에서</p></blockquote><p>스칼라를 도전해본 것은 이번이 처음이 아니다. 함수형에 대해 이름만 알던 차에 <a href="http://www.hanbit.co.kr/ebook/look.html?isbn=9788979149678" title="Functional Programming for Java Developers">자바 개발자를 위한 함수형 프로그래밍</a>를 읽고 함수형이라는 개념을 더 배우고 싶어했고, 코세라(Coursera)에 가입하게 된 계기이자 처음으로(그리고 현재로서는 마지막으로) 수료한 <a href="https://www.coursera.org/course/progfun" title="Functional Programming Principles in Scala">스칼라로 배우는 함수형 프로그래밍 기초</a>로 함수형이라는 패러다임을 잠깐 맛보았다. 그리고 1년 정도 손을 놓았다가 <a href="https://www.coursera.org/course/reactive" title="Principles of Reactive Programming">리액티브 프로그래밍 기초</a>에 다시 도전했다가 영어 실력의 한계와 스칼라의 이해가 부족했다라는 점만 뼈저리게 깨닫고 중도포기를 했다. 그리고 <a href="http://www.bjpublic.co.kr/skin12/product_list.php?boardT=v&amp;page_idx=9&amp;goods_data=aWR4PTk2JnN0YXJ0UGFnZT0zNiZsaXN0Tm89NjEmdGFibGU9cmVkX2dvb2RzJnBhZ2VfaWR4PTkmc2VhcmNoX2l0ZW09%7C%7C" title="Scala for the Impatient">쉽게 배워서 빨리 써먹는 스칼라 프로그래밍</a>를 통해 문법은 리뷰할 수 있었지만 언어 자체와 함수형에 대한 이해도가 좋아지진 않았다.</p><p>스칼라를 더 공부해보고 싶다는 막연한 생각은 가지고 있었고, 함수형에 대한 재미 때문에 underscore/lo-dash를 다양하게 써본다거나 <a href="http://hanbit.co.kr/book/look.html?isbn=978-89-6848-079-9" title="Functional Javascript">함수형 자바스크립트</a> 책을 재미있게 읽기도 했고, 파이썬을 다시 써볼까 싶어서 나간 스터디에서 <a href="http://euler.synap.co.kr/" title="Project Euler">프로젝트 오일러</a>의 문제를 최대한 함수형으로 풀어보면서 더 간결한 코드 작성이 가능해졌다는걸 느꼈다. 그러던 차에 내가 들었던 두 강의의 교수이자 스칼라를 만든 마틴 오더스키의 책, <a href="http://www.acornpub.co.kr/book/programming-in-scala" title="Programming in Scala">스칼라 프로그래밍 2판</a> 번역판이 출간된다는 이야기에 많이 기대하고 있었다. 책 내용과 관련없는 이야기를 길게 늘어놓은 이유는, 내가 생각하기에 나는 초심자와 다름없는 수준같지만 그래도 완전히 스칼라를 처음 접하는 사람과 느끼는 부분이 다를 수 있어서 겪어온 과정을 적어봤다.</p><hr><p>첫번째 장에서는 스칼라의 특징들에 대한 설명과 어떤 언어에서 어떤 장점들을 가져온 것인지에 대해 나열하며 앞으로 배울 것들에 대해 개요를 보여준다. 하지만 언어론에 대해 관심이 있는 사람이 아니라면 적당히 훑어보고 나중에 다시 읽거나 아니면 구조적인 내용(10장 상속과 구성)이 나오기 전까지 읽고 나서 언어에 대한 감을 잡고 다시 1장을 읽어보기를 권한다. 1장을 (체감상)세 호흡 정도로 읽었다면 2장부터 9장까지는 거의 한 호흡에 집중해서 쭉 읽었다.</p><p>2장부터는 필요할 때 REPL로 한줄 정도 테스트해보면서 쭉 읽어나갔다. 그래도 문법에 대해서는 어느 정도 알고 있어서 무난히 넘어가긴 했지만, 이 책으로 처음 스칼라를 접한 사람들에게는 코딩을 시도해볼 여지가 별로 없다. 새로운 언어를 배울 때 이것저것 시도해보면서 체득하는 것이 중요하다고 생각해서 이 책의 그나마 단점이 있다면 그런 면이 아닐까 생각한다. 위에서 언급한 <a href="http://www.bjpublic.co.kr/skin12/product_list.php?boardT=v&amp;page_idx=9&amp;goods_data=aWR4PTk2JnN0YXJ0UGFnZT0zNiZsaXN0Tm89NjEmdGFibGU9cmVkX2dvb2RzJnBhZ2VfaWR4PTkmc2VhcmNoX2l0ZW09%7C%7C" title="Scala for the Impatient">쉽게 배워서~</a>의 경우에는 설명이 빈약해서 그 책만으로 스칼라를 이해하기 어렵지만 챕터마다 연습문제가 있어서 이 책을 읽으면서 같이 진행한다면 좋은 부교재가 되지 않을까 싶다.</p><h3>추천 챕터</h3><p>오랜만에 끝까지 정독한 책이지만 아직 완전히 이해했다고 생각하지 않아서 챕터들에 대해 평가를 내리기 조심스럽지만, 그래도 이 책을 읽을 사람들을 위해서 꼭 두번 읽고 넘어가라고 권해주고 싶은 챕터들이 있어서 메모를 해뒀다.</p><ul><li>12.7 트레이트냐 아니냐, 이것이 문제로다: 어떨 때 트레이트로 만들어야하는지에 대한 가이드라인</li><li>15.1 패턴 매치(p322-324): 패턴매치가 어떻게 작동하는지에 대해 화이트박스로 설명하는 자세한 가이드</li><li>16.10 스칼라의 타입 추론 알고리즘 이해: 어떤 순서와 힌트로 타입을 추론하는지에 대한 매카니즘과 현재 불가능한 점.</li><li>22.1 List 클래스 개괄: 함수형 언어들에서 List(정확히는 cons 구조)가 왜 중요한지, 그리고 이 구조를 이해해야 지연 평가를 이해</li><li>24.15 뷰: Scala를 Scala답게 쓰기 위한 기능(자세히 설명하기 어렵지만 읽고 나면 이해될 것이다)</li></ul><h3>외부 라이브러리 사용</h3><p>그리고 책에서는 <code>scala.actors.Actor</code>가 2.11부터 삭제예정(deprecated)이니 akka를 사용할 것을 권장하고 있는데, 2.11.0부터 아예 패키지 자체가 삭제되었다. 또, 2.11.0부터 독립한 다른 라이브러리들도 있다. <a href="http://www.scala-lang.org/api/2.11.0" title="Scala Standard Library 2.11.0">Scala 2.11.0</a> API 페이지를 보면 다음과 같은 부분이 있다.</p><ul><li>scala.reflect - Scala's reflection API (scala-reflect.jar)</li><li>scala.xml - XML parsing, manipulation, and serialization (scala-xml.jar)</li><li>scala.swing - A convenient wrapper around Java's GUI framework called Swing (scala-swing.jar)</li><li>scala.util.continuations - Delimited continuations using continuation-passing-style (scala-continuations-library.jar, scala-continuations-plugin.jar)</li><li>scala.util.parsing - Parser combinators, including an example implementation of a JSON parser (scala-parser-combinators.jar)</li><li>scala.actors - Actor-based concurrency (deprecated and replaced by Akka actors, scala-actors.jar)</li></ul><p>책을 기준으로 akka를 제외하고 28장 XML 다루기(scala-xml.jar), 33장 콤비네이터 파싱(scala-parser-combinators.jar), 34장 GUI 프로그래밍(scala-swing.jar)이 해당된다. 신기한 것은 REPL로 한줄씩 테스트해볼 때는 몰랐는데, scala-xml이 독립했다는걸 보고 이클립스에서 프로젝트로 만들어서 실험해봤더니 XML 표현법(literal)에서 에러가 났다. REPL(<code>scala</code>)의 경우, bash script 파일인데 여기서 기본 라이브러리(homebrew로 설치한 경우 <code>/usr/local/Cellar/scala/2.11.4/libexec/lib</code>)를 classpath로 추가해서 실행되므로 가능한 것이다.</p><p>외부 라이브러리를 어떻게 사용하는지에 대해 나와있지 않아서 sbt를 사용해보려고 검색하다가 이 책의 역자인 오현석님께서 번역하신 트위터의 <a href="https://twitter.github.io/scala_school/ko/" title="Scala School">스칼라 학교</a>의 <a href="https://twitter.github.io/scala_school/ko/sbt.html" title="Simple Build Tool">빌드 도구 SBT(Simple Build Tool)</a> 챕터를 참고해 시도해보려고 했지만, 간단한 의존성 추가에 너무 많은 공수가 들어서 sbt 레퍼런스에서 <a href="http://www.scala-sbt.org/0.13/tutorial/Library-Dependencies.html" title="Library dependencies">라이브러리 의존성</a>를 참고해 build.sbt을 작성했다. 예를 들어 akka-actor만 추가할 경우에</p><figure class="highlight makefile"><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">name := <span class="string">&quot;Ciruit Simulator&quot;</span></span><br><span class="line"></span><br><span class="line">version := <span class="string">&quot;0.1.0&quot;</span></span><br><span class="line"></span><br><span class="line">scalaVersion := <span class="string">&quot;2.11.4&quot;</span></span><br><span class="line"></span><br><span class="line">libraryDependencies += <span class="string">&quot;com.typesafe.akka&quot;</span> %% <span class="string">&quot;akka-actor&quot;</span> % <span class="string">&quot;2.3.8&quot;</span></span><br></pre></td></tr></table></figure><p>혹은 여러 라이브러리를 한번에 명시할 경우 <code>Seq</code>를 사용해서</p><figure class="highlight go"><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">name := <span class="string">&quot;SCells Spreadsheet&quot;</span></span><br><span class="line"></span><br><span class="line">version := <span class="string">&quot;0.1.0&quot;</span></span><br><span class="line"></span><br><span class="line">scalaVersion := <span class="string">&quot;2.11.4&quot;</span></span><br><span class="line"></span><br><span class="line">libraryDependencies ++= Seq(</span><br><span class="line">    <span class="string">&quot;org.scala-lang&quot;</span> % <span class="string">&quot;scala-swing&quot;</span> % <span class="string">&quot;2.11+&quot;</span>,</span><br><span class="line">    <span class="string">&quot;org.scala-lang.modules&quot;</span> %% <span class="string">&quot;scala-parser-combinators&quot;</span> % <span class="string">&quot;1.0.3&quot;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure><p>이런 식으로 프로젝트 루트에 build.sbt파일을 만들고, <code>sbt update</code>를 통해 가져온 뒤 <code>sbt eclipse</code>를 통해 이클립스에 import할 수 있는 프로젝트를 생성할 수 있다.</p><h3>akka</h3><p>책의 예제는 <code>scala.actors</code>가 기준이라 <code>akka.actor</code>로 예제 소스를 다시 쓰고 싶었지만 제대로 이해하지 못해서 도움이 될만한 글을 검색하던 도중 오현석님의 <a href="http://www.enshahar.me/2014/07/akka.html?view=classic">프로그래밍 인 스칼라 액터 예제 akka로 변환하기(1) - 간단한 액터</a>를 발견해서 이 글과 <a href="http://docs.scala-lang.org/overviews/core/actors-migration-guide.html" title="The Scala Actors Migration Guide">마이그레이션 가이드</a>를 읽고 시도해보려다가 잘 안돼서 다시 검색하던 도중 누군가 만들어준 <a href="https://github.com/drozzy/parallel-discrete-event-akka">프로젝트</a>를 발견했다. 혹시 필요한 분들을 위해 남겨둔다.</p><hr><p>책을 읽으면서 간략하게 메모해놓은 것을 토대로 짧은 후기와 함께 같은 고생을 할 분들을 위해 약간의 도움을 남기고자했는데 의외로 글이 길어졌다. 스칼라를 배우면서 좋았던건 처음에 언급했던 것처럼 함수형이라는 패러다임에 대한 이해에 도움이 된다는 것이었다. 설계부터 변경할 수 없는 값(immutable)이나 지연평가 등 함수형을 잘 살릴 수 있는 구조로 만들수도 있지만, 평소에 하던 스타일에 크게 변형을 주지 않으면서도 세세한 부분의 조금 더 좋게 개선할 수 있는 직교적(orthogonal)인 개념이라는 점에서 누구에게나 도움이 될 수 있지 않을까 생각한다. 다른 패러다임들이 무엇이 있을지 궁금해하던 차에 이 책을 읽는 도중에 발견했던 글이 있는데, AI의 대부격이라는 피터 노빅의 &quot;프로그래밍 10년 완성&quot;이라는 글에서 눈에 들어온 문단을 인용해본다.</p><blockquote><p>프로그래밍을 정복하기 위한 나만의 비법이 있다:</p><ul><li>최소 다섯가지 프로그래밍 언어를 배워라. class abstractions (자바 또는 C++) 지원하는 언어, coroutines을 (Icon 또는 Scheme) 지원하는 언어, functional abstraction (Lisp 또는 ML) 지원하는 언어, syntactic abstraction (Lisp) 지원하는 언어, declarative specifications를 (Prolog또는 C++ 템플렛) 지원하는 언어, 그리고 parallelism을 (Sisal) 지원하는 언어를 한개씩 배워라.</li></ul><p><a href="http://www.norvig.com/21-days.html" title="Teach Yourself Programming in Ten Years">프로그래밍 10년 완성</a>(<a href="http://blog.magicboy.net/entry/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-10%EB%85%84-%EC%99%84%EC%84%B1">번역</a>)에서</p></blockquote><hr><h5>업데이트 01(2015-01-19 02:40)</h5><p><a href="https://groups.google.com/forum/#!forum/scala-korea">라 스칼라 코딩단</a>의 <a href="https://lascala.slack.com/">슬랙 채널</a>을 통해 오현석님의 조언을 받아, akka 부분과 REPL에서 scala-xml이 작동하는 이유에 대해 정정</p><!-- reference and style --><style type="text/css">blockquote p:first-child { text-align: initial !important; }blockquote p:last-child { text-align: right; }</style>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/review/">review</category>
      
      <category domain="http://seoh.blog/tags/scala/">scala</category>
      
      
      <comments>http://seoh.blog/2015/01/18/pis-review/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Framer에서 swipe/drag 제스쳐 프로토타이핑하기</title>
      <link>http://seoh.blog/2014/09/10/prototyping-swipe-and-drag-gestures-with-framer-3/</link>
      <guid>http://seoh.blog/2014/09/10/prototyping-swipe-and-drag-gestures-with-framer-3/</guid>
      <pubDate>Tue, 09 Sep 2014 15:00:00 GMT</pubDate>
      
        
        
      <description>&lt;blockquote&gt;
&lt;h3&gt;Translation of &amp;quot;&lt;a href=&quot;https://medium.com/@gem_ray/prototyping-swipe-and-drag-gestures-with-framer-3-2e405d50b600&quot;&gt;P</description>
        
      
      
      
      <content:encoded><![CDATA[<blockquote><h3>Translation of &quot;<a href="https://medium.com/@gem_ray/prototyping-swipe-and-drag-gestures-with-framer-3-2e405d50b600">Prototyping swipe and drag gestures with Framer 3</a>&quot; into Korean, under the same license as the original.</h3></blockquote><p>우리는 <a href="https://www.potluck.it/">Potluck</a>에서 iOS앱에서 인터렉션을 테스트해보고 프로토타이핑을 하기 위해 Framer.js를 사용하기 시작했다. Framer는 모바일과 데스크탑 앱에서의 인터렉션 프로토타이핑과 에니메이션을 위한 자바스크립트 프레임워크다. 쿼츠 콤포저(Quartz Composer)보다 러닝커브가 훨씬 낮고 아이폰과 데스크탑 양쪽에서 쉽게 실행할 수 있는 훌륭한 대안이다. 키노트에서 시작해서 실제같은 인터렉티브 프로토타입을 단계별로 만들 수 있다.</p><p>자바스크립트 기반이기 때문에 특히 드래그나 스와이프같은 복잡한 제스쳐를 만들 때 좋다. 스와이프 거리에 따라 엘리먼트를 움직이거나 사라지게할 때 혹은 특정거리 이상 스와이프할 때 액션이 발생하도록 할 수 있다.</p><p>신생 프로젝트라 예제나 문서들이 너무 없다. 그래서 배웠던 것들을 공유하려고 생각했다.</p><hr><p>시작하기 전에 타이핑하기 귀찮은 사람을 위해 [CodePen](http://codepen.io/seoh/full/AqycD에 완성본을 올려놨다.</p><h1>Activate on release</h1><p>다른 액션을 가진 테이블 셀들로 간단한 예제를 시작해보자.</p><p>일단 보통 아이폰 스크린을 표시하기 위한 컨테이너 뷰를 만들어보자. 시스템 상태바를 가진 웹앱을 프로토타이핑하기 위해 높이는 598px로 정했다. (역주, 원문에서는 1096px였지만 웹에서 확인하기 좋게 절반으로 줄였다.)</p><p data-height="630" data-theme-id="0" data-slug-hash="teAkE" data-default-tab="js" data-user="seoh" class='codepen'></p><p>셀에서 사용자에게 보이는 부분을 정한 컨테이너를 만들고, 화면 밖에서 슬라이드하면 셀이 나타나도록 할 것이다.</p><p data-height="630" data-theme-id="0" data-slug-hash="hrvAG" data-default-tab="js" data-user="seoh" class='codepen'></p><p>이렇게 보일 것이다.</p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/1.png" alt=""></p><p>이제 셀에 드래그를 추가해보자.</p><p data-height="630" data-theme-id="0" data-slug-hash="lqmjo" data-default-tab="js" data-user="seoh" class='codepen'></p><p>원하는 기능은 아니지만 어디로든 드래그할 수 있다.</p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/2.gif" alt=""></p><hr><p>더 구체적인 제스쳐를 구현하기 위해 드래그 이벤트 핸들러를 사용할 수도 있다. 뭔가 드래그할 때 세가지 이벤트가 발생한다. <code>Events.DragStart</code>는 드래그를 시작할 때 한번, <code>Events.DragEnd</code>는 드래그를 놓았을 때 한번 발생한다. <code>Events.DragMove</code>는 마우스나 손가락을 움직일 때 계속 발생한다.</p><p>draggable은 &quot;speed&quot; 속성을 갖는데 마우스 움직임에 따라 어느 정도의 속도로 움직일지 결정한다. 속도를 <code>0</code>으로 설정하면 물체가 해당하는 방향(축)으로 이동하지 않게 된다.</p><p data-height="630" data-theme-id="0" data-slug-hash="DKtdp" data-default-tab="js" data-user="seoh" class='codepen'></p><p>드래그가 끝났을 때 무엇을 할지 정할 수도 있는데, 여기에서는 원래 물체를 자리로 돌아가게 해보자.</p><p data-height="630" data-theme-id="0" data-slug-hash="GHwLo" data-default-tab="js" data-user="seoh" class='codepen'></p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/3.gif" alt=""></p><hr><p>이제 화면 밖의 엘리먼트가 드래그할 때 나타나도록 해보자. 그 엘리먼트는 셀 컨테이너의 차일드 뷰가 될 것이고(그래서 셀과 함께 움직일 것이고) 그냥 화면 밖에 놓도록 하겠다.</p><p data-height="630" data-theme-id="0" data-slug-hash="liKwL" data-default-tab="js" data-user="seoh" class='codepen'></p><p>이 액션 바를 추가한 다음에 셀을 왼쪽으로 끌면 화면 오른쪽 끝에서 나타나는걸 볼 수 있다.</p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/4.gif" alt=""></p><p>좀 재미있는걸 넣어보자. 드래그 핸들러는 액션 바를 조금이라도 움직일 때마다 실행되는데, 얼마나 셀을 움직이는지에 따라 색깔을 바꾸게 만들 수 있다.</p><p>(얼마나 셀을 움직였는지에 대한)범위를 다른 (바의 색상)범위로 바꾸는 것은 <code>Utils.mapRange</code> 함수를 통해 쉽게 할 수 있다.</p><p>(역주: 원문은 원래 framerjs 2를 기준으로 작성되어 <a href="http://processing.org/">Proccessing 프레임워크</a>의 <code>map_range</code>라는 함수를 사용했는데 중요한 정보는 아니다.)</p><p>이 함수에서는 <code>[low1, high1]</code> 범위에 있는 value를 <code>[low2, high2]</code> 범위에서의 같은 위치(비율)로 변환해준다. 이제 셀의 <em>x축 좌표</em>를 액션 바의 <em>투명도</em>로 바꿔보자.</p><p data-height="630" data-theme-id="0" data-slug-hash="DHBev" data-default-tab="js" data-user="seoh" class='codepen'></p><p>셀을 드래그할 때 액션 바의 색은 다음과 같이 변한다.</p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/5.gif" alt=""></p><p>충분히 액션 바를 드래그했을 때는 그대로 열려있도록 만들고 싶다. 이걸 하려면, DragEnd 핸들러에서 얼마나 드래그되었는지 확인하고 특정 너비를 넘었다면 계속 열려있도록 해보자.</p><p data-height="630" data-theme-id="0" data-slug-hash="qCdrl" data-default-tab="js" data-user="seoh" class='codepen'></p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/6.gif" alt=""></p><h1>Activate on threshold</h1><p>탭을 밀었을 때 계속 열려있도록 해봤다. 탭까지 가지 않고 액션을 할 수는 없을까? iOS 6의 당겨서 새로고침이 좋은 예다. 충분히 당기면 탭까지 가지 않아도 새로고침이 일어난다. (역주, 여기에서는 특정 길이만큼 드래그 후 이벤트 발생으로 애니메이션을 실행하는 것까지만 예제로 한다.)</p><p>위아래로 움직이는 두번째 셀을 만들어보자.</p><p data-height="630" data-theme-id="0" data-slug-hash="udyBe" data-default-tab="js" data-user="seoh" class='codepen'></p><p>셀을 100px 아래로 내리면 쪼그라들게 해보자. 일단 이렇게 시도해봤다.</p><p data-height="630" data-theme-id="0" data-slug-hash="ekgDo" data-default-tab="js" data-user="seoh" class='codepen'></p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/7.gif" alt=""></p><p>애니메이션이 좀 산만하다. 드래그할 때마다 계속 DragMove 이벤트가 발생하고 계속 애니메이션을 시작하려고 한다. 그래서 조건이 만족될 때 딱 한번만 발생하도록 해보자.</p><p data-height="630" data-theme-id="0" data-slug-hash="trboC" data-default-tab="js" data-user="seoh" class='codepen'></p><p><img src="/images/prototyping-swipe-and-drag-gestures-with-framer-3/8.gif" alt=""></p><p>(역주, 원문에서는 Drag와 Animate 기능이 충돌해서 제대로 Animate가 작동하지 않아 Drag하는 레이어와 Animate되는 레이어를 따로 뒀는데 버그가 수정되었는지 잘 작동해서 그 이하 부분은 생략했다.)</p><hr><h1>아이폰에서 테스트</h1><p>Framer Studio 1.7에서 Mirror라는 굉장히 편한 기능이 추가되었다. Studio에서 작업한 내용을 저장하고 Mirror 버튼을 누르면 작업중인 컴퓨터를 자동으로 서버로 사용해서 외부에서 접근할 수 있는 주소를 생성해준다. (역주, 원문에서는 Dropbox Public folder에 대한 내용이었지만 Mirror 기능이 더 유용하다고 생각해서 변경했다.)</p><script async src="http://codepen.io/assets/embed/ei.js"></script>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/framerjs/">framerjs</category>
      
      <category domain="http://seoh.blog/tags/prototyping/">prototyping</category>
      
      
      <comments>http://seoh.blog/2014/09/10/prototyping-swipe-and-drag-gestures-with-framer-3/#disqus_thread</comments>
      
    </item>
    
    <item>
      <title>Coding Dojo #2 후기</title>
      <link>http://seoh.blog/2014/08/09/coding-dojo-2nd/</link>
      <guid>http://seoh.blog/2014/08/09/coding-dojo-2nd/</guid>
      <pubDate>Fri, 08 Aug 2014 15:00:00 GMT</pubDate>
      
        
        
      <description>&lt;p&gt;저번에 이은 &lt;a href=&quot;http://osxdev.org/forum/index.php?threads/8-6-swift-%EC%BD%94%EB%94%A9%EB%8F%84%EC%9E%A5-2%ED%9A%8C-%EA%B3%B5%EC%A7%80.37</description>
        
      
      
      
      <content:encoded><![CDATA[<p>저번에 이은 <a href="http://osxdev.org/forum/index.php?threads/8-6-swift-%EC%BD%94%EB%94%A9%EB%8F%84%EC%9E%A5-2%ED%9A%8C-%EA%B3%B5%EC%A7%80.373/">Coding Dojo</a>에 다녀왔다. 이번 범위는 함수에서 클로저까지.</p><h1>1. 스코프 내의 변수 잡기</h1><blockquote><p>클로저는 사용자의 코드 안에서 전달되거나 사용할 수 있는 기능을 포함한 독립적인 블록(block)입니다. Swift에서의 클로저는 C 및 Objective-C 의 blocks와 유사하며, 다른 언어의 람다(lambda)와도 유사합니다. 클로저는 자신이 정의된 컨텍스트(context)로부터 임의의 상수 및 변수의 참조(reference)를 획득(capture)하고 저장할 수 있습니다.</p><p><a href="http://seoh.github.io/Swift-Korean/#09-closures-">09 클로저 (Closures) by inureyes</a></p></blockquote><p>for-loop(<code>for var...</code>)의 스코프에 있는 변수를 사용하는 함수를 만드는데, 해당하는 변수의 값이 스코프 내에서 계속 변하는 경우에 loop가 끝난 뒤 당연히 마지막값을 기준으로 함수가 실행되는 상황에서 현재값을 저장할 수 있는 방법에 대한 문제였다.</p><p>해결책은</p><ol><li>변수를 다시 캡처한다. <code>var _i = i</code></li><li>for-loop를 돌 때 <code>for var...</code>가 아니라 for-in으로 돌면 상수<code>let</code>가 되어 따로 캡처할 필요가 없다.</li><li>값을 받아서 그 값을 캡처하는 함수를 리턴하는 함수를 만든다.</li></ol><figure class="highlight swift"><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">func</span> <span class="title">capture</span>(<span class="params">i</span>:<span class="type">Int</span>, <span class="params">closure</span>:<span class="type">Int</span>-&gt;<span class="type">Int</span>)</span> -&gt; ()-&gt;<span class="type">Int</span> &#123;  </span><br><span class="line">    <span class="function"><span class="keyword">func</span> <span class="title">sth</span>()</span> -&gt; <span class="type">Int</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> closure(i)</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sth</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// capture(i)&#123; $0*$0 &#125;</span></span><br></pre></td></tr></table></figure><p>3번의 경우를 더 간단하게 만들기 위해서 JavaScript의 즉시실행함수(IIFE)를 흉내내고 싶었는데, <a href="http://stackoverflow.com/questions/25163311/type-inference-of-iife-in-swift">Swift의 버그</a> 때문에 현재 단순히 값만 리턴하는 IIFE도 <code>var</code>는 안되고 <code>let</code>을 써야 타입 추론이 가능하며, let을 쓰더라도 함수를 리턴하는 경우에도 에러가 나고, IIFE 속에서 조건문(conditional statement)이 존재할 경우 경우의 수에 대한 superset을 추론하는게 아니라 그냥 에러가 난다.</p><h1>2. 부분함수와 합성함수의 단계적 구현</h1><h3>2.1. 기본 구현</h3><p>Swift에서도 함수형 프로그래밍 스타일을 지원하도록 Array에 filter, map, reduce 등의 메소드들이 존재해서 array.filter{}.map{}.reduce(){} 같은 스타일로 작성할 수 있다. 그래서 특정 조건까지의 배열을 뽑아서(<code>take</code>) filter와 map을 체이닝해서 변이(tramsform)된 배열을 구하는 문제부터 시작했다. 앞/뒤에서 특정 길이만큼 읽는 함수는 Swift의 기본 라이브러리에서 <code>prefix와</code> <code>suffix</code>를 통해 지원하고 함수형 프로그래밍에서는 보통 <code>take</code>/<code>takeRight라는</code> 이름으로 지원된다. 특정 조건까지 읽는 함수는 <code>takeWhile</code>이라고 보통 부르는데, Swift에서 구현되어있지 않아서 도장에서는 <a href="https://github.com/pNre/ExSwift/#instance-methods">ExSwift</a>에 있는 <code>takeWhile</code>의 소스가 제공되었다.</p><h3>2.2. Pythonic solution</h3><p>Python, underscore.js처럼 filter/map/reduce라는 함수에 sequence를 인자로 넘기는 식으로 개발을 하다보면 체이닝이 아니라 reduce(map(filter(... 처럼 전역함수를 이용해 구현할 수도 있는데, 이럴 경우에는 실행되는 순서와 함수호출의 순서가 역순이고, 값을 평가하는 함수를 인자로 넘길 때 인자로 받는 전역함수와의 거리가 멀어져서 가독성이 떨어지는 문제가 생긴다. 이 문제를 부분 함수(partial function)와 합성 함수(compose function)을 이용해서 인자를 받는 순서를 바꾸면 훨씬 가독성이 올라간다.</p><h3>2.3. Functional Programming</h3><p>함수형 프로그래밍으로 구현하기 위해서는 일단 1급 시민(first-class citizen, 혹은 1급 함수나 1급 객체 등으로 불린다)이라는 개념에 대해 이해가 먼저 필요하다. Java의 경험이 있다면 <a href="http://blog.doortts.com/135">함수형 언어로 가는 길 (중편) - 일급객체</a>, JavaScript의 경험이 있다면 <a href="http://www.nextree.co.kr/p4150/">Javascript : 함수(function) 다시 보기</a>라는 자세한 설명의 글들이 있고 영문으로는 더 자세하고 풍부한 자료들이 있다. 그리고 함수형 프로그래밍에 대한 패러다임의 이해도 필요한데, 가장 좋은 방법은 함수형 프로그래밍을 지원하는 언어를 배우는 것이다. Twitter에서 만든 Scala School이라는 Scala 입문 강의가 있는데 <a href="http://twitter.github.io/scala_school/ko">한글 번역</a>도 존재한다. 혹은 Java 경험이 있다면 <a href="http://www.hanbit.co.kr/ebook/look.html?isbn=9788979149678">자바 개발자를 위한 함수형 프로그래밍</a>라는 eBook을, JavaScript 경험이 있다면 <a href="http://www.hanbit.co.kr/book/look.html?isbn=978-89-6848-079-9">함수형 자바스크립트</a>라는 좋은 책들도 있다.</p><h3>2.4. Solution</h3><p>다시 도장 이야기로 돌아가서, 이 문제를 해결하기 위해 영후님은 F#이라는 언어의 pipe-forwarding(<code>|&gt;</code>)이라는 연산자를 구현하셨다. <a href="http://undefinedvalue.com/2014/07/13/fs-pipe-forward-operator-swift">Swift에서의 pipe-forwarding |&gt;의 구현</a>이라는 글을 읽어보면 구현체와 그 설명이 자세히 나와있다.</p><p>도장에서 나온 문제와 일치하지는 않지만 위에 나온 과정들을 종합해보면 이런 예제를 만들 수 있다.</p><figure class="highlight swift"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">infix</span> <span class="keyword">operator</span> <span class="title">|&gt;</span>   &#123; precedence <span class="number">50</span> associativity left &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="function"><span class="keyword">func</span> <span class="title">|&gt;</span> &lt;<span class="type">T</span>,<span class="type">U</span>&gt;(<span class="params">lhs</span>: <span class="type">T</span>, <span class="params">rhs</span>: <span class="type">T</span> -&gt; <span class="type">U</span>)</span> -&gt; <span class="type">U</span> &#123;  </span><br><span class="line">    <span class="keyword">return</span> rhs(lhs)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">ifilter</span>&lt;<span class="type">T</span>&gt;(<span class="params">closure</span>: <span class="type">T</span>-&gt;<span class="type">Bool</span>)</span> -&gt; [<span class="type">T</span>]-&gt;[<span class="type">T</span>] &#123;  </span><br><span class="line">    <span class="keyword">return</span> &#123; filter(<span class="variable">$0</span>, closure) &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">imap</span>&lt;<span class="type">T</span>,<span class="type">S</span>&gt;(<span class="params">closure</span>: <span class="type">T</span>-&gt;<span class="type">S</span>)</span> -&gt; [<span class="type">T</span>]-&gt;[<span class="type">S</span>] &#123;  </span><br><span class="line">    <span class="keyword">return</span> &#123; map(<span class="variable">$0</span>, closure) &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> list <span class="operator">=</span> [<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 class="operator">|&gt;</span> ifilter  &#123; <span class="variable">$0</span><span class="operator">%</span><span class="number">2</span> <span class="operator">==</span> <span class="number">0</span> &#125;</span><br><span class="line">         <span class="operator">|&gt;</span> imap     &#123; <span class="variable">$0</span><span class="operator">*</span><span class="number">2</span> &#125;</span><br><span class="line">         <span class="operator">|&gt;</span> imap     &#123; <span class="type">String</span>(<span class="variable">$0</span>) &#125;</span><br><span class="line"></span><br><span class="line">println(list) <span class="comment">// [&quot;4&quot;, &quot;8&quot;]  </span></span><br></pre></td></tr></table></figure><h1>3. Accumulator</h1><p>누산기(accumulator)는 함수 하나를 리턴하는데, 그 함수는 인자를 하나 받을 때마다 그 값들이 누산된 결과를 리턴한다. 클로저에서 값을 캡처해서 리턴하는 것은 1번 문제에서 했고, 캡처된 값을 변경하도록 하는 것과 파라미터로 정의된 값을 다시 변수로 사용해서 소스코드를 짧게 만들도록 구현하는게 목적이었다.</p><h1>4. Jensen's Device</h1><p>이번에도 알고리즘 문제가 하나 나왔다. <a href="http://en.wikipedia.org/wiki/Jensen's_Device">Jensen's Device</a>라는 문제로 한 변수를 캡처하고 있는 클로저를 계속 이용해서 따로 변수 선언없이 파라미터만으로 문제를 해결하도록하는 문제였다. 인자로 넘길 때 스코프 명시(<code>&#123;&#125;</code>)없이 쓸 수 있도록 해주는 <code>@auto_closure</code>라는 키워드에 대해 배웠다.</p><h1>5. Conclusion</h1><p>1회의 난이도가 1이었다면 이번 난이도는 10쯤 된다. 다음의 난이도는 얼마나 될지 모르겠다.</p><style type="text/css">blockquote p:first-child { text-align: initial !important; }blockquote p:last-child { text-align: right; }</style>]]></content:encoded>
      
      
      
      <category domain="http://seoh.blog/tags/swift/">swift</category>
      
      <category domain="http://seoh.blog/tags/osxdev/">osxdev</category>
      
      
      <comments>http://seoh.blog/2014/08/09/coding-dojo-2nd/#disqus_thread</comments>
      
    </item>
    
  </channel>
</rss>
