Can someone help break down this example of Function Literals and Closures?

polaris · · 394 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p><a href="https://golang.org/doc/articles/wiki/#tmp_12" rel="nofollow">https://golang.org/doc/articles/wiki/#tmp_12</a></p> <p>The makeHandler function here is in question. To be clear, I get it. I understand what it is doing, and why the author is using it. However, after about a dozen times of combing over the code I am having a tough time understanding the syntax and WHY it is doing what it is doing.</p> <p>Would it be too much to ask for someone provide an overly simplified version of this concept for me with a quick breakdown?</p> <p>FYI, just learning Go. Former C/Obj-C programmer who&#39;s using JS/PHP at my day job.</p> <hr/>**评论:**<br/><br/>earthboundkid: <pre><p>Go lets you make a type out of a function signature, not just a struct/class like other languages. Because of that, an anonymous function that matches the HandlerFunc signature can be promoted to satisfy the Handler interface which is what net/http wants its servers to satisfy. </p></pre>NikkoTheGreeko: <pre><p>Ok that explains why http.HandlerFunc is in there and the main source of confusion. Thanks</p></pre>DenzelM: <pre><p>Let&#39;s break the syntax down step-by-step and make it a little bit simpler as we go along. We&#39;ll start by breaking down the original function:</p> <pre><code>func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { m := validPath.FindStringSubmatch(r.URL.Path) if m == nil { http.NotFound(w, r) return } fn(w, r, m[2]) } } </code></pre> <p>Since you&#39;ve worked with JS, I assume you understand closures. As you said, you&#39;re interested in how the syntax works. So let&#39;s remove all the code concerning title extraction:</p> <pre><code>func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { fn(w, r, m[2]) } } </code></pre> <p>Now, let&#39;s focus exclusively on the definition of <em>makeHandler</em>. It&#39;s a function like any other: accepting a <strong>single</strong> parameter and returning a result. Syntactically, it&#39;s similar to:</p> <pre><code>func addOne(param int) int { return param + 1 } </code></pre> <p><em>makeHandler</em> takes a single argument, <strong>fn</strong>, with a specific type <em>func(http.ResponseWriter, *http.Request, string)</em>. What does this mean? This argument must be another function. And, that other function MUST take three parameters itself with the three specific types given. As you see in the original post, <em>viewHandler</em>, <em>editHandler</em>, and <em>saveHandler</em> all satisfy this type signature.</p> <pre><code>func viewHandler(w http.ResponseWriter, r *http.Request, title string) func editHandler(w http.ResponseWriter, r *http.Request, title string) func saveHandler(w http.ResponseWriter, r *http.Request, title string) </code></pre> <p>Which means that it&#39;s valid for us to pass these functions into <em>makeHandler</em> as an argument, like so:</p> <pre><code>makeHandler(viewHandler) makeHandler(editHandler) makeHandler(saveHandler) </code></pre> <p>If instead we had:</p> <pre><code>func invalidHandler(w http.ResponseWriter, r *http.Request) makeHandler(invalidHandler) // =&gt; type error </code></pre> <p>We&#39;d receive a type error because <em>invalidHandler</em> does not have a third <em>string</em> parameter. Now, let&#39;s go back to the original simplification and see how <em>makeHandler</em> uses its parameter:</p> <pre><code>func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { fn(w, r, m[2]) } } </code></pre> <p><em>makeHandler</em> can invoke whatever function is passed in by calling it like any other function, e.g., <em>fn(...)</em> will invoke the function. But what if we don&#39;t want to invoke the function right now? Well, let&#39;s wrap it in a closure so that it can be invoked later. For example:</p> <pre><code>func example(fn func()) func() { return func() { fn() } } func printHello() { fmt.Println(&#34;Hello&#34;) } funcToInvokeLater := example(printHello) // nothing has printed yet... funcToInvokeLater() // =&gt; prints &#34;Hello&#34; </code></pre> <p>We&#39;ve passed in the <em>printHello</em> function to <em>example</em> which closes over it. It DOES NOT invoke <em>printHello</em> right away, instead it returns a closure (a new function), that, when called, invokes whatever function was passed in.</p> <p>That&#39;s the same principle behind <em>makeHandler</em>. It returns a closure, a new function, that will execute the function passed in later, i.e., when the HTTP handler is invoked. Now, you may be wondering why it&#39;s returning the specific type of function it&#39;s returning. Well that&#39;s because it must meet the <em><a href="https://golang.org/pkg/net/http/#HandlerFunc" rel="nofollow">http.HandlerFunc</a></em> type, defined as:</p> <pre><code>type HandlerFunc func(ResponseWriter, *Request) </code></pre> <p>As you can see, <em>makeHandler</em> does just that by returning an anonymous function with a type signature that matches <em>http.HandlerFunc</em>:</p> <pre><code>func makeHandler(...) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // ... } } </code></pre> <p>Let me know if you have any other questions.</p></pre>NikkoTheGreeko: <pre><p>Fucking brilliant explanation. I may have to put you on speed dial. :) I had a rough idea that is what was going on, but my limited experience with the syntax of Go prevented me from seeing it all. I could have moved on and kept moving through the tutorials (picking back up the pieces I missed later), but I want to intimately understand each piece I come across and you have definitely helped me with this one.</p></pre>DenzelM: <pre><p>I&#39;m glad it was helpful! You&#39;re doing the right thing by working to intimately understand each piece, especially since there aren&#39;t many pieces to Go. And once you get them all down, you&#39;re fluency will increase your productivity exponentially.</p> <p>If you have anymore questions in the future, feel free to email me denzel &#34;dot&#34; morris 1 &#34;at&#34; that google mail service. I try to provide best-effort guidance to as many fellow gophers as I can (ones that prove they&#39;ve done their due diligence and ask well-formulated questions like you).</p> <p>Best of luck with your learning. </p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

394 次点击  
加入收藏 微博
0 回复
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传