Đăng ký Đăng nhập

Tài liệu Clojure programming

.PDF
630
81
101

Mô tả:

www.it-ebooks.info www.it-ebooks.info Clojure Programming Chas Emerick, Brian Carper, and Christophe Grand Beijing • Cambridge • Farnham • Köln • Sebastopol • Tokyo www.it-ebooks.info Clojure Programming by Chas Emerick, Brian Carper, and Christophe Grand Copyright © 2012 Chas Emerick, Brian Carper, and Christophe Grand. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://my.safaribooksonline.com). For more information, contact our corporate/institutional sales department: (800) 998-9938 or [email protected]. Editors: Mike Loukides and Julie Steele Production Editor: Teresa Elsey Copyeditor: Nancy Reinhardt Proofreader: Linley Dolby April 2012: Indexer: Fred Brown Cover Designer: Karen Montgomery Interior Designer: David Futato Illustrator: Robert Romano First Edition. Revision History for the First Edition: 2012-03-28 First release See http://oreilly.com/catalog/errata.csp?isbn=9781449394707 for release details. Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks of O’Reilly Media, Inc. Clojure Programming, the image of a painted snipe, and related trade dress are trademarks of O’Reilly Media, Inc. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and O’Reilly Media, Inc., was aware of a trademark claim, the designations have been printed in caps or initial caps. While every precaution has been taken in the preparation of this book, the publisher and authors assume no responsibility for errors or omissions, or for damages resulting from the use of the information contained herein. ISBN: 978-1-449-39470-7 [LSI] 1332955528 www.it-ebooks.info Table of Contents Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi 1. Down the Rabbit Hole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 Why Clojure? Obtaining Clojure The Clojure REPL No, Parentheses Actually Won’t Make You Go Blind Expressions, Operators, Syntax, and Precedence Homoiconicity The Reader Scalar Literals Comments Whitespace and Commas Collection Literals Miscellaneous Reader Sugar Namespaces Symbol Evaluation Special Forms Suppressing Evaluation: quote Code Blocks: do Defining Vars: def Local Bindings: let Destructuring (let, Part 2) Creating Functions: fn Conditionals: if Looping: loop and recur Referring to Vars: var Java Interop: . and new Exception Handling: try and throw Specialized Mutation: set! Primitive Locking: monitor-enter and monitor-exit Putting It All Together 1 3 3 6 7 9 12 13 18 19 19 20 20 23 23 24 25 26 27 28 36 42 43 44 44 45 45 45 46 iii www.it-ebooks.info eval This Is Just the Beginning 46 48 Part I. Functional Programming and Concurrency 2. Functional Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 What Does Functional Programming Mean? On the Importance of Values About Values Comparing Values to Mutable Objects A Critical Choice First-Class and Higher-Order Functions Applying Ourselves Partially Composition of Function(ality) Writing Higher-Order Functions Building a Primitive Logging System with Composable Higher-Order Functions Pure Functions Why Are Pure Functions Interesting? Functional Programming in the Real World 52 52 53 54 58 59 65 68 71 72 76 78 81 3. Collections and Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Abstractions over Implementations Collection Sequences Associative Indexed Stack Set Sorted Concise Collection Access Idiomatic Usage Collections and Keys and Higher-Order Functions Data Structure Types Lists Vectors Sets Maps Immutability and Persistence Persistence and Structural Sharing Transients Metadata iv | Table of Contents www.it-ebooks.info 84 87 89 99 103 104 105 106 111 112 113 114 114 115 117 117 122 123 130 134 Putting Clojure’s Collections to Work Identifiers and Cycles Thinking Different: From Imperative to Functional Navigation, Update, and Zippers In Summary 136 137 138 151 157 4. Concurrency and Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Shifting Computation Through Time and Space Delays Futures Promises Parallelism on the Cheap State and Identity Clojure Reference Types Classifying Concurrent Operations Atoms Notifications and Constraints Watches Validators Refs Software Transactional Memory The Mechanics of Ref Change The Sharp Corners of Software Transactional Memory Vars Defining Vars Dynamic Scope Vars Are Not Variables Forward Declarations Agents Dealing with Errors in Agent Actions I/O, Transactions, and Nested Sends Using Java’s Concurrency Primitives Locking Final Thoughts 160 160 162 163 166 168 170 172 174 176 176 178 180 180 181 191 198 198 201 206 208 209 212 214 224 225 226 Part II. Building Abstractions 5. Macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229 What Is a Macro? What Macros Are Not What Can Macros Do that Functions Cannot? Macros Versus Ruby eval 229 231 232 234 Table of Contents | v www.it-ebooks.info Writing Your First Macro Debugging Macros Macroexpansion Syntax quote Versus syntax-quote unquote and unquote-splicing When to Use Macros Hygiene Gensyms to the Rescue Letting the User Pick Names Double Evaluation Common Macro Idioms and Patterns The Implicit Arguments: &env and &form &env &form Testing Contextual Macros In Detail: -> and ->> Final Thoughts 235 237 237 239 240 241 243 244 246 248 249 250 251 252 254 258 259 262 6. Datatypes and Protocols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263 Protocols Extending to Existing Types Defining Your Own Types Records Types Implementing Protocols Inline Implementation Reusing Implementations Protocol Introspection Protocol Dispatch Edge Cases Participating in Clojure’s Collection Abstractions Final Thoughts 264 266 270 272 277 280 281 285 289 290 292 299 7. Multimethods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 Multimethods Basics Toward Hierarchies Hierarchies Independent Hierarchies Making It Really Multiple! A Few More Things Multiple Inheritance Introspecting Multimethods type Versus class; or, the Revenge of the Map vi | Table of Contents www.it-ebooks.info 301 304 306 308 311 313 313 314 314 The Range of Dispatch Functions Is Unlimited Final Thoughts 316 317 Part III. Tools, Platform, and Projects 8. Organizing and Building Clojure Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321 Project Geography Defining and Using Namespaces Location, Location, Location The Functional Organization of Clojure Codebases Build Ahead-of-Time Compilation Dependency Management The Maven Dependency Management Model Build Tools and Configuration Patterns Final Thoughts 321 322 332 334 336 337 339 339 344 353 9. Java and JVM Interoperability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355 The JVM Is Clojure’s Foundation Using Java Classes, Methods, and Fields Handy Interop Utilities Exceptions and Error Handling Escaping Checked Exceptions with-open, finally’s Lament Type Hinting for Performance Arrays Defining Classes and Implementing Interfaces Instances of Anonymous Classes: proxy Defining Named Classes Annotations Using Clojure from Java Using deftype and defrecord Classes Implementing Protocol Interfaces Collaborating Partners 356 357 360 362 364 364 366 370 371 372 374 381 385 388 390 392 10. REPL-Oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393 Interactive Development The Persistent, Evolving Environment Tooling The Bare REPL Eclipse Emacs 393 397 398 399 403 405 Table of Contents | vii www.it-ebooks.info Debugging, Monitoring, and Patching Production in the REPL Special Considerations for “Deployed” REPLs Limitations to Redefining Constructs In Summary 411 414 415 417 Part IV. Practicums 11. Numerics and Mathematics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421 Clojure Numerics Clojure Prefers 64-bit (or Larger) Representations Clojure Has a Mixed Numerics Model Rationals The Rules of Numeric Contagion Clojure Mathematics Bounded Versus Arbitrary Precision Unchecked Ops Scale and Rounding Modes for Arbitrary-Precision Decimals Ops Equality and Equivalence Object Identity (identical?) Reference Equality (=) Numeric Equivalence (==) Optimizing Numeric Performance Declare Functions to Take and Return Primitives Use Primitive Arrays Judiciously Visualizing the Mandelbrot Set in Clojure 421 422 422 424 425 427 428 430 432 433 433 434 435 436 438 442 449 12. Design Patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 Dependency Injection Strategy Pattern Chain of Responsibility Aspect-Oriented Programming Final Thoughts 459 462 463 466 470 13. Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471 Immutable Values and Pure Functions Mocking clojure.test Defining Tests Test “Suites” Fixtures Growing an HTML DSL Relying upon Assertions viii | Table of Contents www.it-ebooks.info 471 472 473 474 477 479 481 486 Preconditions and Postconditions 487 14. Using Relational Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491 clojure.java.jdbc with-query-results Explained Transactions Connection Pooling Korma Prelude Queries Why Bother with a DSL? Hibernate Setup Persisting Data Running Queries Removing Boilerplate Final Thoughts 491 494 496 496 498 498 499 500 503 503 506 506 507 509 15. Using Nonrelational Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511 Getting Set Up with CouchDB and Clutch Basic CRUD Operations Views A Simple (JavaScript) View Views in Clojure _changes: Abusing CouchDB as a Message Queue À la Carte Message Queues Final Thoughts 512 512 514 514 516 520 522 525 16. Clojure and the Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 527 The “Clojure Stack” The Foundation: Ring Requests and Responses Adapters Handlers Middleware Routing Requests with Compojure Templating Enlive: Selector-Based HTML Transformation Final Thoughts 527 529 529 531 532 534 535 545 546 554 17. Deploying Clojure Web Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 557 Java and Clojure Web Architecture Web Application Packaging 557 560 Table of Contents | ix www.it-ebooks.info Running Web Apps Locally Web Application Deployment Deploying Clojure Apps to Amazon’s Elastic Beanstalk Going Beyond Simple Web Application Deployment 565 566 567 570 Part V. Miscellanea 18. Choosing Clojure Type Definition Forms Wisely . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 19. Introducing Clojure into Your Workplace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 577 Just the Facts… Emphasize Productivity Emphasize Community Be Prudent 577 579 580 582 20. What’s Next? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583 (dissoc Clojure 'JVM) ClojureCLR ClojureScript 4Clojure Overtone core.logic Pallet Avout Clojure on Heroku 583 583 584 584 585 585 586 587 587 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589 x | Table of Contents www.it-ebooks.info Preface Clojure is a dynamically and strongly typed programming language hosted on the Java Virtual Machine (JVM), now in its fifth year. It has seen enthusiastic adoption by programmers from a variety of backgrounds, working in essentially all problem domains. Clojure offers a compelling mix of features and characteristics applicable to solving modern programming challenges: • Functional programming foundations, including a suite of persistent data structures with performance characteristics approaching typical mutable data structures • A mature, efficient runtime environment, as provided by the host JVM • JVM/Java interoperability capabilities suited for a wide variety of architectural and operational requirements • A set of mechanisms providing reliable concurrency and parallelism semantics • A Lisp pedigree, thereby providing remarkably flexible and powerful metaprogramming facilities Clojure offers a compelling practical alternative to many who strain against the limitations of typical programming languages and environments. We aim to demonstrate this by showing Clojure seamlessly interoperating with existing technologies, libraries, and services that many working programmers already use on a day-to-day basis. Throughout, we’ll provide a solid grounding in Clojure fundamentals, starting from places of common expertise and familiarity rather than from (often foreign) computer science first principles. Who Is This Book For? We wrote this book with a couple of audiences in mind. Hopefully, you consider yourself a part of one of them. Clojure matches and often exceeds your current favorite language’s expressivity, concision, and flexibility while allowing you to effortlessly leverage the performance, libraries, community, and operational stability of the JVM. This makes it a natural next step for Java developers (and even JVM developers using interpreted or otherwise not xi www.it-ebooks.info particularly fast non-Java languages), who simply will not accept a performance hit or who do not want to give up their JVM platform investment. Clojure is also a natural step for Ruby and Python developers who refuse to compromise on language expressivity, but wish they had a more reliable, efficient execution platform and a larger selection of quality libraries. Engaged Java Developers There are millions of Java developers in the world, but some fewer number are working in demanding environments solving nontrivial, often domain-specific problems. If this describes you, you’re probably always on the hunt for better tools, techniques, and practices that will boost your productivity and value to your team, organization, and community. In addition, you’re probably at least somewhat frustrated with the constraints of Java compared to other languages, but you continue to find the JVM ecosystem compelling: its process maturity, massive third-party library selection, vendor support, and large skilled workforce is hard to walk away from, no matter how shiny and appealing alternative languages are. You’ll find Clojure to be a welcome relief. It runs on the JVM with excellent performance characteristics, interoperates with all of your existing libraries, tools, and applications, and is simpler than Java, yet is demonstrably more expressive and less verbose. Ruby, Python, and Other Developers Ruby and Python are not new languages by any means, but they have garnered significant (dare we say, “mainstream”?) traction over recent years. It’s not hard to see why: both are expressive, dynamic languages that, along with their thriving communities, encourage maximal developer productivity in many domains. Clojure is a natural next step for you. As a Ruby or Python programmer, you’re probably unwilling to compromise on their strengths, but you may wish for a more capable execution platform, better runtime performance, and a larger selection of libraries. The fact that Clojure is efficiently hosted on the JVM fulfills those desires—and it matches or exceeds the degrees of language sophistication and developer productivity that you’ve come to expect. We will frequently compare and contrast Clojure with Java, Ruby, and Python to help you translate your existing expertise to Clojure. In such comparisons, we will always refer to the canonical implementations of these other languages: • Ruby MRI (also called CRuby) • CPython • Java 6/7 xii | Preface www.it-ebooks.info How to Read This Book In formulating our approach to this book, we wanted to provide a fair bit of concrete detail and practical examples that you could relate to, but stay clear of what we thought were generally unsuccessful approaches for doing so. In particular, we’ve been frustrated in the past by books that attempted to thread the implementation of a single program or application through their pages. Such approaches seem to result in a disjointed narrative, as well as the dominance of a tortured “practical” example that may or may not apply or appeal to readers. With that in mind, we split the book in two, starting with foundational, instructional narrative that occupies roughly two-thirds of the book, followed in Part IV by a number of discrete, practical examples from real-world domains. This clear segmentation of content with decidedly distinct objectives may qualify this book as a “duplex book.” (This term may have been coined by Martin Fowler in http://martinfowler.com/bliki/ DuplexBook.html.) In any case, we can conceive of two obvious approaches to reading it. Start with Practical Applications of Clojure Often the best way to learn is to dig straight into the nitty-gritty of how a language is used in the real world. If that sounds appealing, the hope is that you will find that at least a couple of the practicums resonate with what you do on a day-to-day basis, so that you can readily draw parallels between how you solve certain categories of problems in your current language(s) and how they may be solved using Clojure. You’re going to bump into a lot of potentially foreign concepts and language constructs in those chapters—when you do, use that context within the domain in question as your entry point for understanding those concepts using the relevant instructional material in the first part of the book. Start from the Ground Up with Clojure’s Foundational Concepts Sometimes the only way to truly understand something is to learn it inside-out, starting with the fundamentals. If you prefer that approach, then you will likely find that digesting this book starting from the first page of Chapter 1 will be best. We have attempted to provide a comprehensive treatment of all of Clojure’s foundational principles and constructs in a narrative that progresses such that it will be very rare for you to need to look ahead in the book to understand concepts in earlier sections. As you begin to get a handle on Clojure’s fundamentals, feel free to jump ahead into the practicums you find most interesting and relevant to your work. Preface | xiii www.it-ebooks.info Who’s “We”? We are three software developers who have each taken different paths in coming to use and appreciate Clojure. In writing this book, we have attempted to distill all that we’ve learned about why and how you should use Clojure so that you can be successful in your use of it as well. Chas Emerick Chas has been a consistent presence in the Clojure community since early 2008. He has made contributions to the core language, been involved in dozens of Clojure open source projects, and frequently writes and speaks about Clojure and software development generally. Chas maintains the Clojure Atlas (http://clojureatlas.com), an interactive visualization of and learning aid for the Clojure language and its standard libraries. The founder of Snowtide (http://snowtide.com), a small software company in Western Massachusetts, Chas’s primary domain is unstructured data extraction, with a particular specialty around PDF documents. He writes about Clojure, software development, entrepreneurship, and other passions at http://cemerick.com. Brian Carper Brian is a Ruby programmer turned Clojure devotee. He’s been programming Clojure since 2008, using it at home and at work for everything from web development to data analysis to GUI apps. Brian is the author of Gaka (https://github.com/briancarper/gaka), a Clojure-to-CSS compiler, and Oyako (https://github.com/briancarper/oyako), an Object-Relational Mapping library. He writes about Clojure and other topics at http://briancarper.net. Christophe Grand Christophe was a long-time enthusiast of functional programming lost in Java-land when he encountered Clojure in early 2008, and it was love at first sight! He authored Enlive (http://github.com/cgrand/enlive), an HTML/XML transformation, extraction, and templating library; Parsley (http://github.com/cgrand/parsley), an incremental parser generator; and Moustache (http://github.com/cgrand/moustache), a routing and middleware application DSL for Ring. As an independent consultant, he develops, coaches, and offers training in Clojure. He also writes about Clojure at http://clj-me.cgrand.net. xiv | Preface www.it-ebooks.info Acknowledgments Like any sizable piece of work, this book would not exist without the tireless efforts of dozens, probably hundreds of people. First, Rich Hickey, the creator of Clojure. In just a few short years, he has designed, implemented, and shepherded a new programming language into the world that, for so many, has been not just another tool, but a reinvigoration of our love of programming. Beyond that, he’s personally taught us a great deal—certainly about programming, but also about patience, humility, and perspective. Thanks, Rich. Dave Fayram and Mike Loukides were essential in helping to formulate the initial concept and approach of the book. Of course, you likely wouldn’t be reading this book right now if it weren’t for Julie Steele, our editor, and all of the fine people at O’Reilly who took care of the logistics and minutiae that go along with publishing. The quality of this book would be far less than it is were it not for the efforts of our technical reviewers, including Sam Aaron, Antoni Batchelli, Tom Faulhaber, Chris Granger, Anthony Grimes, Phil Hagelberg, Tom Hicks, Alex Miller, William Morgan, Laurent Petit, and Dean Wampler. We’d also like to thank all of those who provided feedback and comments on the early releases and Rough Cuts of the book, both on the O’Reilly forums and via email, Twitter, and so on. Michael Fogus and Chris Houser have inspired us in many ways large and small. One of the smaller ways was the style and presentation of the REPL interactions in their Clojure book, The Joy of Clojure, which we shamelessly copied and iterated. If we’ve neglected to mention anyone, please accept our implicit thanks and our apologies; at the end of this endeavor, we are quite lucky to be upright and coherent at all! And Last, but Certainly Far from Least The Clojure community has been my home away from home for a number of years. The hospitality and positive, helpful energy I see anywhere Clojure programmers congregate continues to be an inspiration and example to me. In particular, many of the regular denizens of #clojure on Freenode IRC—in addition to becoming good friends—have guided me toward learning things I never would have otherwise. To my coauthors, Christophe and Brian: working with you has been a great honor for me. There is absolutely no way that I would have been able to complete this work without you. To my parents, Charley and Darleen: my compulsive curiosity about how things work, my love of language and rhetoric, and my interest in business—all of these can be traced back over the years to your consistent influence. Without it, I am certain I would not have found my unique path, started a software company, or written this book, each done against all odds. Preface | xv www.it-ebooks.info Finally, to my wife, Krissy: the sacrifices you’ve made to enable me to chase my ambitions are legion. It is likely that I’ll never be able to thank you sufficiently. So, I’ll just say: I love you. —Chas Emerick, February 2012 To everyone in the community who helped create Clojure: thank you for your tireless hard work, for making my professional and personal coding life so much more enjoyable, and for opening my eyes to what’s possible. To my coauthors, Christophe and Chas: I’ve never worked with a smarter group of people. It’s been an honor and a privilege. To my wife Nicole: sorry I kept you awake all night with my typing. —Brian Carper, February 2012 To Rich Hickey for creating Clojure and fostering such a friendly community. To this community for having brought me to higher standards. To my coauthors, Brian and Chas: it has been a great honor to work with you. A mon professeur Daniel Goffinet, et à ses exercices improbables, qui a radicalement changé mon approche de la programmation et de l’informatique—sur ces sujets je lui suis plus redevable qu’à nul autre. (To Pr. Daniel Goffinet, and his meta mind twisters, who radically altered the way I think about programming and computing—on those subjects there is no one I’m more indebted to.) A mes parents pour votre amour bien sûr mais aussi pour tout le temps à s’inquiéter que je passais trop de temps sur l’Amstrad. (To my parents: for your love obviously and for buying me that 8-bit computer you worried I was spending too much time on.) A ma compagne Emilie, et mon fils Gaël, merci d’être là et de m’avoir supporté pendant l’écriture de ce livre. (To my wife Emilie and to my son Gaël: thank you for being there and having supported me throughout the writing of this book.) —Christophe Grand, February 2012 Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. xvi | Preface www.it-ebooks.info Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. ; listing lines prefixed with a semicolon Used to indicate content printed (i.e., to standard out/err) by code evaluated in the REPL. ;= listing lines prefixed with a semicolon + equal sign Used to indicate the result/return value of a REPL evaluation. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values determined by context. This icon signifies a tip, suggestion, or general note. This icon indicates a warning or caution. Using Code Examples This book is here to help you get your job done. In general, you may use the code in this book in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing a CD-ROM of examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but do not require, attribution. An attribution usually includes the title, author, publisher, and ISBN. For example: “Clojure Programming by Chas Emerick, Brian Carper, and Christophe Grand (O’Reilly). Copyright 2012 Chas Emerick, Brian Carper, and Christophe Grand, 978-1-449-39470-7.” If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at [email protected]. Preface | xvii www.it-ebooks.info Safari® Books Online Safari Books Online is an on-demand digital library that lets you easily search over 7,500 technology and creative reference books and videos to find the answers you need quickly. With a subscription, you can read any page and watch any video from our library online. Read books on your cell phone and mobile devices. Access new titles before they are available for print, and get exclusive access to manuscripts in development and post feedback for the authors. Copy and paste code samples, organize your favorites, download chapters, bookmark key sections, create notes, print out pages, and benefit from tons of other time-saving features. O’Reilly Media has uploaded this book to the Safari Books Online service. To have full digital access to this book and others on similar topics from O’Reilly and other publishers, sign up for free at http://my.safaribooksonline.com. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-998-9938 (in the United States or Canada) 707-829-0515 (international or local) 707-829-0104 (fax) We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at: http://shop.oreilly.com/product/0636920013754.do To comment or ask technical questions about this book, send email to: [email protected] For more information about our books, courses, conferences, and news, see our website at http://www.oreilly.com. Find us on Facebook: http://facebook.com/oreilly Follow us on Twitter: http://twitter.com/oreillymedia Watch us on YouTube: http://www.youtube.com/oreillymedia xviii | Preface www.it-ebooks.info
- Xem thêm -

Tài liệu liên quan