├── core └── src │ └── main │ └── scala │ ├── package.scala │ └── rillit │ ├── Lens.scala │ └── Lenser.scala ├── project └── build.scala ├── src └── main │ └── scala │ └── rillit │ └── Lens.scala ├── example └── src │ └── main │ └── scala │ └── example │ └── TestLens.scala ├── README.md └── LICENSE /core/src/main/scala/package.scala: -------------------------------------------------------------------------------- 1 | import language.implicitConversions 2 | 3 | package object rillit { 4 | implicit def lenser2lens[A,B](lenser: InitializedLenser[A,B]) = lenser.apply 5 | } 6 | -------------------------------------------------------------------------------- /core/src/main/scala/rillit/Lens.scala: -------------------------------------------------------------------------------- 1 | package rillit 2 | 3 | trait Lens[A, B] { 4 | def get(x: A): B 5 | 6 | def set(x: A, v: B): A 7 | 8 | def modify(a:A, f: B => B) : A = set(a, f(get(a))) 9 | 10 | def compose[C](that: Lens[C,A]) = new Lens[C,B] { 11 | def get(c: C) = Lens.this.get(that.get(c)) 12 | def set(c: C, b: B) = that.modify(c, Lens.this.set(_, b)) 13 | } 14 | 15 | def andThen[C](that: Lens[B,C]) = that compose this 16 | } -------------------------------------------------------------------------------- /project/build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | 4 | object build extends Build { 5 | val sharedSettings = Defaults.defaultSettings ++ Seq( 6 | organization := "fi.akisaarinen", 7 | version := "0.1.1-SNAPSHOT", 8 | scalaVersion := "2.10.0-RC5", 9 | //scalacOptions ++= Seq("-Xlog-free-terms", "-Ymacro-debug-lite"), 10 | libraryDependencies <+= (scalaVersion)("org.scala-lang" % "scala-compiler" % _), 11 | libraryDependencies <+= (scalaVersion)("org.scala-lang" % "scala-reflect" % _) 12 | ) 13 | 14 | lazy val core = Project( 15 | id = "rillit", 16 | base = file("core"), 17 | settings = sharedSettings ++ Seq( 18 | publishTo := Some(Resolver.file("file", new File("../rillit-gh-pages/maven"))) 19 | ) 20 | ) 21 | 22 | lazy val example = Project( 23 | id = "example", 24 | base = file("example"), 25 | settings = sharedSettings, 26 | dependencies = Seq(core) 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/rillit/Lens.scala: -------------------------------------------------------------------------------- 1 | package rillit 2 | 3 | import language.experimental.macros 4 | 5 | case class Lens[A,B]( 6 | get: A => B, 7 | set: (A, B) => A 8 | ) { 9 | def modify(a:A, f: B => B) : A = set(a, f(get(a))) 10 | 11 | def compose[C](that: Lens[C,A]) = Lens[C,B]( 12 | c => get(that.get(c)), 13 | (c, b) => that.modify(c, set(_, b)) 14 | ) 15 | 16 | def andThen[C](that: Lens[B,C]) = that compose this 17 | } 18 | 19 | case class Inner(value: Int) 20 | case class Outer(inner: Inner) 21 | 22 | object Main { 23 | 24 | def main(args: Array[String]) { 25 | val outer = Outer(Inner(3)) 26 | val mod1 = outer.copy(inner = outer.inner.copy(value = outer.inner.value + 1)) 27 | 28 | val valueLens = Lens[Outer, Int]( 29 | get = _.inner.value, 30 | set = (o, v) => o.copy(inner = o.inner.copy(value = v)) 31 | ) 32 | 33 | println(valueLens.get(outer)) 34 | println(valueLens.set(outer, 4)) 35 | 36 | val value = Lens[Inner, Int]( 37 | get = _.value, 38 | set = (i, v) => i.copy(value = v) 39 | ) 40 | val inner = Lens[Outer, Inner]( 41 | get = _.inner, 42 | set = (o, i) => o.copy(inner = i) 43 | ) 44 | 45 | val composedLens = inner andThen value 46 | println(composedLens.get(outer)) 47 | println(composedLens.set(outer, 4)) 48 | 49 | } 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /example/src/main/scala/example/TestLens.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import rillit._ 4 | 5 | object Main { 6 | // A simple instance of Person class used for examples 7 | val person = Person( 8 | name = Name("Aki", "Saarinen"), 9 | contact = Contact( 10 | email = Email("aki", "akisaarinen.fi"), 11 | web = "http://akisaarinen.fi" 12 | ) 13 | ) 14 | 15 | // Simplest possible 16 | def getterExample() { 17 | val lens = Lenser[Person].contact.email 18 | println("Getter example:") 19 | println(" Email: %s".format(lens.get(person))) // 'aki@akisaarinen.fi' 20 | } 21 | 22 | // Use lens created on-the-fly to set a value into nested case classes. 23 | // The traditional way of doing this without lenses is this: 24 | // val updated = person.copy(contact = person.contact.copy(email = something)) 25 | def setterExample() { 26 | val updated = Lenser[Person].contact.email.set(person, Email("foo", "foobar.com")) 27 | 28 | println("Setter example:") 29 | println(" Original person: %s".format(person)) // email = 'aki@akisaarinen.fi' 30 | println(" Updated person: %s".format(updated)) // email = 'foo@foobar.com' 31 | } 32 | 33 | // Here we create two simple lenses and demonstrate the ability to compose 34 | // them; this is very useful in practice with functional lenses. Of course in 35 | // this case we could just `Lenser[Person].contact.email.user` but that wouldn't 36 | // demonstrate composition :) 37 | def lensCompositionExample() { 38 | val user = Lenser[Email].user 39 | val email = Lenser[Person].contact.email 40 | 41 | val lens = email andThen user 42 | 43 | println("Composed lens example:") 44 | println(" Getter: %s".format(lens.get(person))) // 'aki' 45 | println(" Setter: %s".format(lens.set(person, "john"))) // email = 'john@akisaarinen.fi' 46 | } 47 | 48 | case class Person(name: Name, contact: Contact) 49 | case class Name(first: String, last: String) 50 | case class Contact(email: Email, web: String) 51 | case class Email(user: String, domain: String) { 52 | override def toString = "%s@%s".format(user, domain) 53 | } 54 | 55 | def main(args: Array[String]) { 56 | getterExample() 57 | setterExample() 58 | lensCompositionExample() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/scala/rillit/Lenser.scala: -------------------------------------------------------------------------------- 1 | package rillit 2 | 3 | import language.experimental.macros 4 | import language.dynamics 5 | import scala.reflect.macros._ 6 | 7 | trait InitializedLenser[A, B] extends Dynamic { 8 | def apply(): Lens[A, B] 9 | 10 | def selectDynamic(propName: String) = macro Lenser.selectDynamic[A,B] 11 | } 12 | 13 | case class Lenser[A]() extends Dynamic { 14 | def selectDynamic(propName: String) = macro Lenser.initializeBuilderImpl[A] 15 | } 16 | 17 | object Lenser { 18 | def build[A, B](lens: Lens[A,B]) = new InitializedLenser[A,B] { 19 | def apply() = lens 20 | } 21 | 22 | def initializer[A] = new Lenser[A] 23 | 24 | def initializeBuilderImpl[T: c.WeakTypeTag](c: Context)(propName: c.Expr[String]) = { 25 | import c.universe._ 26 | 27 | def abort(reason: String) = c.abort(c.enclosingPosition, reason) 28 | 29 | val t = (c.prefix.tree, propName.tree) match { 30 | case (x, Literal(Constant(name: String))) => 31 | val lensSourceTpe = c.weakTypeOf[T] 32 | val calledMember = lensSourceTpe.member(newTermName(name)) orElse { 33 | abort("value %s is not a member of %s".format(name, lensSourceTpe)) 34 | } 35 | val lensTargetTpe = calledMember.typeSignatureIn(lensSourceTpe) match { 36 | case NullaryMethodType(tpe) => tpe 37 | case _ => abort("member %s is not a field".format(name)) 38 | } 39 | val lens = createLens[T](c)(lensSourceTpe, lensTargetTpe, name) 40 | createBuilder(c)(lens, lensSourceTpe, lensTargetTpe, name) 41 | 42 | case x => 43 | abort("unexpected c.prefix tree: %s".format(x)) 44 | } 45 | c.Expr[Any](c.resetAllAttrs(t)) 46 | } 47 | 48 | 49 | def selectDynamic[A: c.WeakTypeTag, B: c.WeakTypeTag](c: Context { type PrefixType = InitializedLenser[A, B] })(propName: c.Expr[String]) = { 50 | import c.universe._ 51 | 52 | def abort(reason: String) = c.abort(c.enclosingPosition, reason) 53 | 54 | val t = (c.prefix.tree, propName.tree) match { 55 | case (x, Literal(Constant(name: String))) => 56 | val lensSourceTpe = c.weakTypeOf[B] 57 | 58 | val calledMember = lensSourceTpe.member(newTermName(name)) orElse { 59 | abort("value %s is not a member of %s".format(name, lensSourceTpe)) 60 | } 61 | val lensTargetTpe = calledMember.typeSignatureIn(lensSourceTpe) match { 62 | case NullaryMethodType(tpe) => tpe 63 | case _ => abort("member %s is not a field".format(name)) 64 | } 65 | 66 | val parentApply = x.tpe.member(newTermName("apply")) 67 | val parentLens = Apply(Select(x, parentApply), List()) 68 | val lens = createLens[B](c)(lensSourceTpe, lensTargetTpe, name) 69 | val combinedLens = Apply(Select(lens, newTermName("compose")), List(parentLens)) 70 | 71 | createBuilder(c)(combinedLens, c.weakTypeOf[A], lensTargetTpe, name) 72 | 73 | case x => 74 | abort("unexpected c.prefix tree: %s".format(x)) 75 | } 76 | c.Expr[Any](c.resetAllAttrs(t)) 77 | } 78 | 79 | def createBuilder(c: Context)(lens: c.universe.Tree, originalBuilderTpe: c.universe.Type, lensTargetTpe: c.universe.Type, name: String) = { 80 | import c.universe._ 81 | 82 | val constructor = 83 | DefDef( 84 | Modifiers(), 85 | nme.CONSTRUCTOR, 86 | List(), 87 | List(List()), 88 | TypeTree(), 89 | Block( 90 | List(Apply(Select(Super(This(""), ""), nme.CONSTRUCTOR), Nil)), 91 | Literal(Constant(())))) 92 | 93 | val applyF = 94 | DefDef( 95 | Modifiers(), newTermName("apply"), List(), 96 | List(), 97 | TypeTree(), 98 | lens 99 | ) 100 | 101 | Block( 102 | List( 103 | ClassDef(Modifiers(Flag.FINAL), newTypeName("$anon"), List(), 104 | Template(List( 105 | AppliedTypeTree( 106 | Ident(c.mirror.staticClass("rillit.InitializedLenser")), List(TypeTree(originalBuilderTpe), TypeTree(lensTargetTpe)))), 107 | emptyValDef, List( 108 | constructor, 109 | applyF 110 | )) 111 | )), 112 | Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()) 113 | ) 114 | } 115 | 116 | 117 | def createLens[T: c.WeakTypeTag](c: Context)(lensTpe: c.universe.Type, memberTpe: c.universe.Type, name: String) = { 118 | import c.universe._ 119 | 120 | def mkParam(name: TermName, tpe: Type) = 121 | ValDef(Modifiers(Flag.PARAM), name, TypeTree(tpe), EmptyTree) 122 | 123 | def createDef(name: TermName, params: List[(TermName, Type)], body: Tree): Tree = { 124 | DefDef( 125 | Modifiers(), name, List(), 126 | List(params.map { case (name, tpe) => mkParam(name, tpe) }), 127 | TypeTree(), 128 | body 129 | ) 130 | } 131 | 132 | val constructor = createDef( 133 | name = nme.CONSTRUCTOR, 134 | params = List(), 135 | body = Block(List(Apply(Select(Super(This(""), ""), nme.CONSTRUCTOR), Nil)), Literal(Constant(()))) 136 | ) 137 | 138 | val getF = createDef( 139 | name = newTermName("get"), 140 | params = List(newTermName("x$") -> lensTpe), 141 | body = Select(Ident(newTermName("x$")), newTermName(name)) 142 | ) 143 | 144 | val setF = createDef( 145 | name = newTermName("set"), 146 | params = List(newTermName("x$") -> lensTpe, newTermName("v$") -> memberTpe), 147 | body = Apply( 148 | Select(Ident(newTermName("x$")), newTermName("copy")), 149 | List(AssignOrNamedArg(Ident(newTermName(name)), Ident(newTermName("v$")))) 150 | ) 151 | ) 152 | 153 | Block( 154 | List( 155 | ClassDef(Modifiers(Flag.FINAL), newTypeName("$anon"), List(), 156 | Template(List( 157 | AppliedTypeTree( 158 | Ident(c.mirror.staticClass("rillit.Lens")), List(TypeTree(lensTpe), TypeTree(memberTpe)))), 159 | emptyValDef, List( 160 | constructor, 161 | getF, 162 | setF 163 | )) 164 | )), 165 | Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List()) 166 | ) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rillit 2 | ====== 3 | 4 | Rillit provides experimental functional lenses for Scala 2.10. 5 | 6 | Right now it is mostly an experiment in providing minimum effort creation of 7 | lenses using a `Lenser`, implemented with Scala 2.10 macros and `Dynamic`. 8 | Longer-term aim of `rillit` is to be a stand-alone functional lens 9 | implementation. 10 | 11 | Here is my [blog 12 | post](http://akisaarinen.fi/blog/2012/12/07/boilerplate-free-functional-lenses-for-scala/) 13 | explaining shortly what Rillit does. 14 | 15 | Installation 16 | ============ 17 | 18 | Rillit is published in a Maven repository if you want to try it out. 19 | However, please note that rillit is still very much an experimental project 20 | (*everything* may break, or not even work in the first place). 21 | 22 | Add the following to your SBT project to start experimenting with rillit: 23 | 24 | ``` 25 | resolvers += "rillit-repository" at "http://akisaarinen.github.com/rillit/maven" 26 | 27 | libraryDependencies += "fi.akisaarinen" % "rillit_2.10" % "0.1.0" 28 | ``` 29 | 30 | Why? 31 | ==== 32 | 33 | Functional lenses are composable getters and setters for immutable data 34 | structures, i.e. usually case classes in Scala. 35 | 36 | Say you have the following case class structure for describing a person. The 37 | nesting seems unncessary in this small example, but that's what you need to do 38 | with larger data structures, so bear with me: 39 | 40 | ```scala 41 | case class Email(user: String, domain: String) 42 | case class Contact(email: Email, web: String) 43 | case class Person(name: String, contact: Contact) 44 | 45 | val person = Person( 46 | name = "Aki Saarinen", 47 | contact = Contact( 48 | email = Email("aki", "akisaarinen.fi"), 49 | web = "http://akisaarinen.fi" 50 | ) 51 | ) 52 | ``` 53 | 54 | Now, say you want to modify the `user` of the email address from 'aki' 55 | to 'john'. And because we're working with immutable data structures, we can't 56 | just assign a new value, but we want to create a new instance of `Person` with 57 | the `user` field updated. 58 | 59 | This pattern comes up very often when writing functional code with immutable 60 | data structures. 61 | 62 | Using pure Scala, you would do this: 63 | 64 | ``` 65 | scala> person.copy(contact = person.contact.copy(email = person.contact.email.copy(user = "john"))) 66 | res0: Person = Person(Aki Saarinen,Contact(Email(john,akisaarinen.fi),http://akisaarinen.fi)) 67 | ``` 68 | 69 | The field gets updated, but the syntax is very verbose and ugly. 70 | 71 | Functional lenses can ease the situation. Rillit provides a `Lenser`, which 72 | creates a new functional lens for your `user` field, hence making its update an 73 | easy task: 74 | 75 | ``` 76 | scala> Lenser[Person].contact.email.user.set(person, "john") 77 | res1: Person = Person(Aki Saarinen,Contact(Email(john,akisaarinen.fi),http://akisaarinen.fi)) 78 | ``` 79 | 80 | This performs the exact same thing as our long nested `copy` soup above, but 81 | looks a lot more civilized. 82 | 83 | There is a whole lot more we can do with lenses (i.e. you can for example 84 | compose your lenses together, forming new lenses), but just solving this case 85 | is great on its own. Rillit focuses on implementing a boilerplate-free `Lenser` 86 | for the lens creation. 87 | 88 | Difference to other implementations 89 | =================================== 90 | 91 | The lenses themselves are very bare-bones here, the main point is to 92 | demonstrate the ability to create lenses in a boilerplate-free way for nested 93 | case classes. `Lenser` does just that, using macros and 94 | `Dynamic`. 95 | 96 | Lens features included in e.g. Scalaz or Shapeless lenses could be combined 97 | with the functionality of `Lenser`, to make lens both lens creation and usage 98 | as convenient as possible. At the moment of writing this, creation of lenses in 99 | both Scalaz and Shapeless contains more boilerplate than in Rillit. 100 | 101 | An example use of `Lenser` (which does not actually produce a `Lens` but a `Lenser[A,B]` 102 | which can be implicitly converted to `Lens[A,B]`): 103 | 104 | ```scala 105 | val lens = Lenser[Person].contact.email 106 | ``` 107 | 108 | Also, this being a very early proof-of-concept experiment, the code is not very 109 | pretty (luckily there's not very much of it). 110 | 111 | A longer example 112 | ================ 113 | 114 | ```scala 115 | package example 116 | 117 | import rillit._ 118 | 119 | object Main { 120 | // A simple instance of Person class used for examples 121 | val person = Person( 122 | name = Name("Aki", "Saarinen"), 123 | contact = Contact( 124 | email = Email("aki", "akisaarinen.fi"), 125 | web = "http://akisaarinen.fi" 126 | ) 127 | ) 128 | 129 | // Simplest possible 130 | def getterExample() { 131 | val lens = Lenser[Person].contact.email 132 | println("Getter example:") 133 | println(" Email: %s".format(lens.get(person))) // 'aki@akisaarinen.fi' 134 | } 135 | 136 | // Use lens created on-the-fly to set a value into nested case classes. 137 | // The traditional way of doing this without lenses is this: 138 | // val updated = person.copy(contact = person.contact.copy(email = something)) 139 | def setterExample() { 140 | val updated = Lenser[Person].contact.email.set(person, Email("foo", "foobar.com")) 141 | 142 | println("Setter example:") 143 | println(" Original person: %s".format(person)) // email = 'aki@akisaarinen.fi' 144 | println(" Updated person: %s".format(updated)) // email = 'foo@foobar.com' 145 | } 146 | 147 | // Here we create two simple lenses and demonstrate the ability to compose 148 | // them; this is very useful in practice with functional lenses. Of course in 149 | // this case we could just `Lenser[Person].contact.email.user` but that wouldn't 150 | // demonstrate composition :) 151 | def lensCompositionExample() { 152 | val user = Lenser[Email].user 153 | val email = Lenser[Person].contact.email 154 | 155 | val lens = email andThen user 156 | 157 | println("Composed lens example:") 158 | println(" Getter: %s".format(lens.get(person))) // 'aki' 159 | println(" Setter: %s".format(lens.set(person, "john"))) // email = 'john@akisaarinen.fi' 160 | } 161 | 162 | case class Person(name: Name, contact: Contact) 163 | case class Name(first: String, last: String) 164 | case class Contact(email: Email, web: String) 165 | case class Email(user: String, domain: String) { 166 | override def toString = "%s@%s".format(user, domain) 167 | } 168 | 169 | def main(args: Array[String]) { 170 | getterExample() 171 | setterExample() 172 | lensCompositionExample() 173 | } 174 | } 175 | ``` 176 | 177 | When run, this will produce the following: 178 | 179 | ``` 180 | Getter example: 181 | Email: aki@akisaarinen.fi 182 | Setter example: 183 | Original person: Person(Name(Aki,Saarinen),Contact(aki@akisaarinen.fi,http://akisaarinen.fi)) 184 | Updated person: Person(Name(Aki,Saarinen),Contact(foo@foobar.com,http://akisaarinen.fi)) 185 | Composed lens example: 186 | Getter: aki 187 | Setter: Person(Name(Aki,Saarinen),Contact(john@akisaarinen.fi,http://akisaarinen.fi)) 188 | ``` 189 | 190 | Requirements 191 | ============ 192 | 193 | * Scala 2.10 (tested with 2.10.0-RC5, but will probably work with older release candidates as well) 194 | * SBT 0.12 195 | * A bit of love for functional lenses 196 | 197 | Usage 198 | ===== 199 | 200 | Rillit is not currently distributed as a library, as this is just an experiment on 201 | how the functional lenses could be implemented. To try it out, install SBT 0.12, 202 | and just run the example with: 203 | 204 | ``` 205 | sbt "project rillit-testing" run 206 | ``` 207 | 208 | Caveats 209 | ======= 210 | 211 | Macros are an *experimental* feature of 2.10, so it is probably not a good idea 212 | to use something like this in critical production code just yet. Also, the code 213 | is not very pretty as-is, it's just a proof-of-concept I made to convince 214 | myself that this is even possible with `Dynamic` and macros. 215 | 216 | Inspiration 217 | =========== 218 | 219 | Following projects motivated me to create Rillit, either because of their lens 220 | implementations, use of Scala macros, or both: 221 | 222 | * Scalaz: https://github.com/scalaz/scalaz 223 | * Shapeless: https://github.com/milessabin/shapeless 224 | * Macrocosm: https://github.com/retronym/macrocosm 225 | * sqltyped: https://github.com/jonifreeman/sqltyped 226 | 227 | License 228 | ======= 229 | 230 | Rillit is released under the Apache License, Version 2.0. 231 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------