Repository: nrinaudo/scala-best-practices Branch: master Commit: 9712d9721d05 Files: 88 Total size: 277.4 KB Directory structure: gitextract_y_x3cf17/ ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.sbt ├── project/ │ ├── MdocSitePlugin.scala │ ├── build.properties │ └── plugins.sbt └── src/ ├── main/ │ └── mdoc/ │ ├── _adts/ │ │ ├── data_constructors_in_companion_object.md │ │ ├── enumerations_as_adt.md │ │ ├── errors_extend_exception.md │ │ ├── final_case_objects.md │ │ ├── index.md │ │ └── product_with_serializable.md │ ├── _binary_compat/ │ │ ├── abstract_over_trait.md │ │ ├── explicit_type_annotations.md │ │ └── index.md │ ├── _definitions/ │ │ ├── adt.md │ │ ├── binary_compatibility.md │ │ ├── final.md │ │ ├── implicit_scope.md │ │ ├── paren-less.md │ │ ├── recursion.md │ │ ├── reference_equality.md │ │ ├── referential_transparency.md │ │ ├── sealed.md │ │ ├── tail_recursion.md │ │ ├── total_function.md │ │ └── type_class.md │ ├── _oop/ │ │ ├── abstract_fields_as_defs.md │ │ ├── always_override.md │ │ └── index.md │ ├── _partial_functions/ │ │ ├── either_projection_get.md │ │ ├── index.md │ │ ├── option_get.md │ │ ├── traversable_head.md │ │ ├── traversable_init.md │ │ ├── traversable_last.md │ │ ├── traversable_reduce.md │ │ ├── traversable_tail.md │ │ └── try_get.md │ ├── _referential_transparency/ │ │ ├── avoid_mutability.md │ │ ├── avoid_return.md │ │ ├── avoid_throwing_exceptions.md │ │ └── index.md │ ├── _tricky_behaviours/ │ │ ├── final_case_classes.md │ │ ├── future_in_comprehensions.md │ │ ├── implicit_shadowing.md │ │ ├── index.md │ │ ├── leaky_sealed_types.md │ │ ├── string_concatenation.md │ │ ├── type_implicits.md │ │ └── unicode_operators.md │ ├── _unsafe/ │ │ ├── array_comparison.md │ │ ├── avoid_null.md │ │ ├── checking_empty_collection.md │ │ ├── custom_extractors.md │ │ ├── implicit_conversions.md │ │ ├── index.md │ │ ├── recursion.md │ │ ├── structural_types.md │ │ └── tail_recursion.md │ ├── _warming_up/ │ │ ├── checking_for_nan.md │ │ ├── checking_for_odd.md │ │ ├── index.md │ │ └── numeric_literals.md │ └── index.md └── site/ ├── _config.yml ├── _includes/ │ ├── footer.html │ ├── header.html │ ├── linters.html │ └── toc.html ├── _layouts/ │ ├── article.html │ ├── compress.html │ ├── definition.html │ ├── index.html │ └── section.html ├── css/ │ ├── fira_code/ │ │ └── fira_code.css │ ├── fontawesome/ │ │ └── font-awesome.css │ ├── inter/ │ │ └── inter.css │ └── styles.css └── fonts/ ├── fira/ │ ├── LICENSE │ ├── fira.css │ └── otf/ │ ├── FiraMono-Bold.otf │ ├── FiraMono-Medium.otf │ └── FiraMono-Regular.otf └── fontawesome/ ├── FontAwesome.otf └── font-awesome.css ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.class *.log # sbt specific .cache/ .history/ .lib/ dist/* target/ lib_managed/ src_managed/ project/boot/ project/plugins/project/ # Scala-IDE specific .scala_dependencies .worksheet # Ensime .ensime .ensime_cache # Intellij .idea/ *.iml *.iws # Mac .DS_Store # Auto-generated files .scalafmt.conf scalastyle-config.xml ================================================ FILE: .travis.yml ================================================ language: scala dist: trusty sudo: false cache: directories: - $HOME/.ivy2/cache - $HOME/.sbt/boot/ - $HOME/.cache/coursier before_cache: # Tricks to avoid unnecessary cache updates - find $HOME/.ivy2 -name "ivydata-*.properties" -delete - find $HOME/.sbt -name "*.lock" -delete scala: - 2.12.8 jdk: - oraclejdk8 script: - sbt makeSite ================================================ FILE: LICENSE ================================================ Attribution 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: README.md ================================================ # Building the site ## Required tools The following tools are needed to build the site: * [sbt], to compile and assemble all examples * [jekyll], to turn the output into a static site ## Steps You first need to run all pages through the amazing [mdoc](https://github.com/scalameta/mdoc) to make sure all examples are valid: ```shell sbt makeSite ``` This will generate [jekyll] sources in `./target/site`. For local development / browsing, the simplest thing to do is to run the following from `./target/site`: ```shell jekyll serve --watch -c _config.yml --baseurl "" ``` This will make the entire site available on `http://localhost:4000`. [jekyll]:https://jekyllrb.com/ [sbt]:https://www.scala-sbt.org/ ================================================ FILE: build.sbt ================================================ enablePlugins(MdocSitePlugin, GhpagesPlugin) scalaVersion := "2.12.8" scalacOptions ++= Seq("-feature", "-language:implicitConversions", "-language:reflectiveCalls") mdocIn := (sourceDirectory in Compile).value / "mdoc" mdocExtraArguments += "--no-link-hygiene" includeFilter in makeSite := "*.yml" | "*.md" | "*.html" | "*.css" | "*.png" | "*.jpg" | "*.gif" | "*.js" | "*.eot" | "*.svg" | "*.ttf" | "*.woff" | "*.woff2" | "*.otf" git.remoteRepo := "git@github.com:nrinaudo/scala-best-practices.git" ghpagesNoJekyll := false ================================================ FILE: project/MdocSitePlugin.scala ================================================ import com.typesafe.sbt.site.SitePlugin.autoImport._ import mdoc.MdocPlugin, MdocPlugin.autoImport._ import sbt._, Keys._ import sbt.plugins.{JvmPlugin, SbtPlugin} /** Provides glue to integrate mdoc with sbt-site. */ object MdocSitePlugin extends AutoPlugin { override def trigger = allRequirements override def requires = JvmPlugin && MdocPlugin object autoImport { val mdocSite = taskKey[Seq[(File, String)]]("create mdoc documentation in a way that lets sbt-site grab it") val mdocSiteOut = settingKey[String]("name of the directory in which sbt-site will store mdoc documentation") } import autoImport._ override def projectSettings: Seq[Setting[_]] = Seq( mdocSite := { mdoc.toTask(" ").value val out = mdocOut.value for { (file, name) <- out ** AllPassFilter --- out pair Path.relativeTo(out) } yield file -> name }, mdocSiteOut := "./", addMappingsToSiteDir(mdocSite, mdocSiteOut) ) } ================================================ FILE: project/build.properties ================================================ sbt.version=1.2.6 ================================================ FILE: project/plugins.sbt ================================================ addSbtPlugin("org.scalameta" % "sbt-mdoc" % "1.2.7") addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2") addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2") ================================================ FILE: src/main/mdoc/_adts/data_constructors_in_companion_object.md ================================================ --- title: Declare ADT data constructors in the companion object layout: article --- > When writing the various data constructors of an [ADT], put them in the companion object of the root type rather than at the top level. For example, prefer: ```scala mdoc sealed abstract class Option[+A] extends Product with Serializable object Option { final case class Some[A](value: A) extends Option[A] final case object None extends Option[Nothing] } ``` To: ```scala mdoc:reset sealed abstract class Option[+A] extends Product with Serializable final case class Some[A](value: A) extends Option[A] final case object None extends Option[Nothing] ``` Yes, I'm aware that this example contradicts the Scala stdlib - that's on purpose. I have [Martin Odersky](https://twitter.com/odersky) on record for recommending exactly the approach that [`Option`] did not take. # Reason Putting data constructors in the companion object can be locally made to behave as if they'd been declared at the top level: just import them in. ```scala mdoc sealed abstract class Foo extends Product with Serializable object Foo { final case object Bar extends Foo final case object Baz extends Foo } // This brings Bar and Baz in the local scope, exactly as if // they'd been declared at the top level. import Foo._ Bar ``` It's however impossible to get top level data constructors to behave as if they'd been declared in the companion object. It's possible to hide or rename them, but this requires one instruction per data constructor, which is verbose and doesn't really scale with large [ADTs][ADT]: ```scala mdoc:reset object root { sealed abstract class Foo extends Product with Serializable final case object Bar extends Foo final case object Baz extends Foo } // This renames Bar to FooBar and imports Foo and Baz into the // local scope. import root.{Bar => FooBar, _} FooBar ``` And while you might argue that you'd rarely need to do that, names are a surprisingly scarce resource. You *will* eventually get in naming conflicts - how many times have I written error types that conflicted with [`Failure`]? This rule helps minimise such conflicts by namespacing all data constructors. [`Option`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html [`Failure`]:https://www.scala-lang.org/api/2.12.8/scala/util/Failure.html [ADT]:../definitions/adt.html ================================================ FILE: src/main/mdoc/_adts/enumerations_as_adt.md ================================================ --- title: Use ADTs to implement enumerations layout: article linters: - name: wartremover rules: - name: Enumeration url: http://www.wartremover.org/doc/warts.html#enumeration --- > When you need to implement an enumeration, use an [ADT], not [`Enumeration`]. # Reason [`Enumeration`] suffers from a major flaw: exhaustivity is not checked in pattern matches. For example: ```scala mdoc object Status extends Enumeration { val Ok, Nok = Value } def foo(w: Status.Value): Unit = w match { case Status.Ok => println("ok") } ``` This compiles without a warning but will blow up at runtime: ```scala mdoc:crash foo(Status.Nok) ``` [ADTs][ADT], on the other hand, do not suffer from this issue. Here's a better implementation of `Status`: ```scala mdoc:reset sealed abstract class Status extends Product with Serializable object Status { final case object Ok extends Status final case object Nok extends Status } ``` The compiler now has enough information to check whether your pattern matches are exhaustive: ```scala mdoc:fail def foo(w: Status): Unit = w match { case Status.Ok => println("ok") } ``` [`Enumeration`]:https://www.scala-lang.org/api/2.12.8/scala/Enumeration.html [ADT]:../definitions/adt.html ================================================ FILE: src/main/mdoc/_adts/errors_extend_exception.md ================================================ --- title: Make error ADTs subtypes of Exception layout: article --- > When creating an [ADT] to represent errors, have the base type extend [`Exception`]. # Reason Some APIs, such as the standard [`Try`] and [`Future`], represent errors through exceptions. If your error type doesn't extend [`Exception`], you cannot stick it in a [`Failure`], for example. Let's say we have this simple [ADT] for database-related errors: ```scala mdoc sealed abstract class DbError extends Product with Serializable object DbError { final case object InvalidSql extends DbError final case object ConnectionLost extends DbError } ``` And for whatever reason, we need to implement the following method: ```scala def foo(i: Int): Try[Int] ``` With our current `DbError` implementation, it's impossible to write something like: ```scala mdoc:fail def foo(i: Int) = scala.util.Failure(DbError.InvalidSql) ``` Had `DbError` extended [`Exception`] however, this would have been perfectly possible: ```scala mdoc:reset sealed abstract class DbError extends Exception with Product with Serializable object DbError { final case object InvalidSql extends DbError final case object ConnectionLost extends DbError } def foo(i: Int) = scala.util.Failure(DbError.InvalidSql) ``` # Secondary reason There's another reason for this rule, although I find it dubious. Imagine you have two different error [ADTs][ADT] - our `DbError` one, and another that's more about business logic: ```scala mdoc sealed abstract class UserError extends Exception with Product with Serializable object UserError { final case object NotFound extends UserError final case object Unauthorized extends UserError } ``` If you try to write a function that attempts to find a user in database and validate its password, its error type is either a `DbError` or a `UserError`. There are ways to represent that - [`Either`] (but that gets messy very quickly if we have more than 2 error types) or a unifying [ADT] that wraps both types, for instance. Had both `DbError` and `UserError` extended [`Exception`], we'd have a third option: [`Exception`] being a sort of universal supertype for errors, we could just say that our error type is [`Exception`] and the compiler would be perfectly happy. I personally dislike this approach. It removes the compiler's ability to check whether you've dealt with all error cases. You must manually choose which subtypes of [`Exception`] you want to deal with, and the compiler has no way of knowing if you've dealt with all error types for a given call, or if the types you deal with are actually possible in a given context. [`Exception`]:https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Exception.html [`Try`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html [`Future`]:https://www.scala-lang.org/api/2.12.8/scala/concurrent/Future.html [`Failure`]:https://www.scala-lang.org/api/2.12.8/scala/util/Failure.html [`Either`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either.html [ADT]:../definitions/adt.html ================================================ FILE: src/main/mdoc/_adts/final_case_objects.md ================================================ --- title: Mark case objects as final layout: article --- > When declaring a case object, make it [`final`]. # Reason While case objects are effectively [`final`] in Scala - you can't extend them - they are not always flagged as such in the compiled bytecode. Take the following code, for example: ```scala mdoc class Test { case object foo } ``` When `foo` is not flagged as [`final`], running the class file through `javap -c` yields the following (truncated) output: ```java public class Test$foo$ implements scala.Product,scala.Serializable { // [...] ``` When it's flagged as [`final`], however, we get: ```java public final class Test$foo$ implements scala.Product,scala.Serializable { // [...] ``` To make matters worse, this is not even coherent accross Scala versions - the previous output was obtained with 2.12.7, but 2.11.8, for example, will compile both versions of `Test.foo` as [`final`]. This might not seem like a big deal - as far as `scalac` is concerned, `Test.foo` is [`final`] either way and you won't be able to extend it - not having this information in the bytecode will: * prevent the JIT from applying any [`final`]-specific optimisations * potentially cause Java interop issues, since `javac` will let you extend `Test.foo`. [`final`]:../definitions/final.html ================================================ FILE: src/main/mdoc/_adts/index.md ================================================ --- layout: section --- [ADTs][adt] are a common and powerful way of representing data. Scala encodes them in a way that is (currently) a bit manual and heavy on the boilerplate, and there are a few rules to respect in order for the resulting types to be well behaved and idiomatic. This is what this section will be focusing on. [adt]:../definitions/adt.html ================================================ FILE: src/main/mdoc/_adts/product_with_serializable.md ================================================ --- title: Make ADTs subtypes of Product and Serializable layout: article --- > When writing an [ADT], always have the root type extend [`Product`] and [`Serializable`]. # Reason It allows the compiler to infer better types than it would otherwise. Given the following simple [ADT]: ```scala mdoc sealed abstract class Status object Status { final case object Ok extends Status final case object Nok extends Status } ``` The compiler will sometimes infer some wonky types: ```scala mdoc List(Status.Ok, Status.Nok) ``` This happens because inferring types is done by looking at the most specific supertype of `Status.Ok` and `Status.Nok`. `Status` *is* a supertype of both, but it's not the most specific one: `Status.Ok` and `Status.Nok` both also extend [`Product`] and [`Serializable`] by virtue of being case objects. As far as the compiler is concerned, the most precise supertype it can find for both `Status.Ok` and `Status.Nok` is thus `Product with Serializable with Status`. Now, consider the following implementation - the only difference is in the types that `Status` extend: ```scala mdoc:reset sealed abstract class Status extends Product with Serializable object Status { final case object Ok extends Status final case object Nok extends Status } ``` Here, the most specific supertype of both `Status.Ok` and `Status.Nok` is `Status`, which leads to the correct types being inferred: ```scala mdoc List(Status.Ok, Status.Nok) ``` [ADT]:../definitions/adt.html [`Product`]:https://www.scala-lang.org/api/2.12.8/scala/Product.html [`Serializable`]:https://www.scala-lang.org/api/2.12.8/scala/Serializable.html ================================================ FILE: src/main/mdoc/_binary_compat/abstract_over_trait.md ================================================ --- title: Prefer abstract classes to traits layout: article --- > When declaring an abstract type without a clear reason to prefer a `trait`, use an `abstract class`. Since in most scenarios that aren't declaring an [ADT](../definitions/adt.html), it's impossible to guarantee that your type will never be involved in multiple inheritance, this rule can mostly be simplified to: > When writing an [ADT](../definitions/adt.html), have the root type be an abstract class # Reasons ## Binary compatibility Adding a new concrete method to an existing trait breaks [binary compatibility](../definitions/binary_compatibility.html), which is a big deal for libraries. The reason this happens has [been explained](https://stackoverflow.com/questions/18366817/is-adding-a-trait-method-with-implementation-breaking-backward-compatibility) far better than I could hope to do. ## Java interop Traits can be a bit painful when you're writing Java code that has Scala dependencies. One concrete example of this is companion objects: ```scala mdoc trait ATrait object ATrait { def foo(): Int = 1 } abstract class AnAbtractClass object AnAbstractClass { def foo(): Int = 2 } ``` With these definitions, calling `foo` from Java code will look like: ```java ATrait$.MODULE$.foo() AnAbstractClass.foo() ``` Clearly, the abstract class version is more natural. ## It's a good default The previous reasons don't really matter to projects that are not libraries or will never have to be used from Java code. But abstract classes are not a worse choice than traits in most other scenarios (again, except when multiple inheritance is a possibility), and I prefer: > When in doubt, use an abstract class. to > When in doubt and writing code that is or might become a library or is or might be called from Java, use an abstract class. ================================================ FILE: src/main/mdoc/_binary_compat/explicit_type_annotations.md ================================================ --- title: Add explicit type annotations to public members layout: article linters: - name: wartremover rules: - name: PublicInference url: http://www.wartremover.org/doc/warts.html#publicinference --- > Always add an explicit type to your public members, even when you're happy with what's being inferred. # Reason The compiler relies on implementation details to infer types, and implementation details can change - which can in turn break [binary compatibility][bincompat]. Type inference tries to work out the most specific subtype of all types a value could be. Take the following code, for example: ```scala mdoc def foo(i: Int) = { if(i % 2 == 0) Some(i) else None } foo(1) ``` [`Some[Int]`][`Some`] and [`None`] share a common direct supertype: [`Option[Int]`][`Option`] (well, [not quite](../adts/product_with_serializable.html), but close enough for our purposes). This allows the compiler to correctly infer `foo`'s return type, but what if we were to change the implementation in a later version? ```scala mdoc:reset def foo(i: Int) = Some(i) foo(1) ``` The return type is no longer [`Option[Int]`][`Option`] but [`Some[Int]`][`Some`], and just like that, we've broken [binary compatibility][bincompat]. Explicit type annotations on public members ensure that implementation details don't leak out and that we don't accidentally break things without meaning to, or even realizing. # Mistaken assumption: implementing abstract members A common assumption that turns out to be incorrect is that implementing abstract members (or overriding concrete ones) is fine, since the parent type will be inferred. This turns out to be incorrect: ```scala mdoc abstract class Foo { def getOpt[A](a: A): Option[A] } class FooImpl extends Foo { override def getOpt[A](a: A) = Some(a) } ``` It's usually assumed that `FooImpl.getOpt` will return the right type, but this is wrong: ```scala mdoc new FooImpl().getOpt(1) ``` Changing `FooImpl.getOpt` implementation to return either a [`Some[Int]`][`Some`] or an [`Option[Int]`][`Option`] will break [binary compatibility][bincompat] just as much as the general case. [`Option`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html [`Some`]:https://www.scala-lang.org/api/2.12.8/scala/Some.html [`Any`]:https://www.scala-lang.org/api/2.12.8/scala/Any.html [bincompat]:../definitions/binary_compatibility.html [`None`]:https://www.scala-lang.org/api/2.12.8/scala/None$.html ================================================ FILE: src/main/mdoc/_binary_compat/index.md ================================================ --- layout: section --- [Binary compatibility][bincompat] is a matter of some importance to library authors - breaking it must only be done when there is no other choice, as it can have dire repercussions downstream. We'll explore here a few perfectly innocent design decisions that can, and often will, eventually break [binary compatibility][bincompat] without the author realising it's happened. [bincompat]:../definitions/binary_compatibility.html ================================================ FILE: src/main/mdoc/_definitions/adt.md ================================================ --- title: Algebraic Data Types layout: definition --- Algebraic Data Types (ADTs for short) are a way of structuring data. They're widely used in Scala due, mostly, to how well they work with pattern matching and how easy it is to use them to make illegal states impossible to represent. There are two basic categories of ADTs: * product types * sum types # Product types A product type is essentially a way of sticking multiple values inside of one - a [`Tuple`], or something that's very similar to one. Case classes are the prototypical product type: ```scala mdoc final case class Foo(b1: Boolean, b2: Boolean) ``` `Foo` aggregates two [`Boolean`] values. It's called a _product_ type because we can compute its cardinality (the number of values it can possibly have) by calculating the product of the types that compose it. Here, [`Boolean`] has an cardinality of 2 (it can only contain either `true` or `false`), and thus `Foo` must have an cardinality of 4. Indeed: * `Foo(true, true)` * `Foo(true, false)` * `Foo(false, true)` * `Foo(false, false)` # Sum types A sum type is a type that is composed of different possible values and value shapes. The simplest possible example is an enumeration - `Bool`, for example: ```scala mdoc sealed abstract class Bool extends Product with Serializable object Bool { final case object True extends Bool final case object False extends Bool } ``` It's called a _sum_ type because its cardinality is equal to the sum of the arities of the types that compose it. Here, both `True` and `False` are singleton types, and `Bool` can indeed only have 2 possible values. Sum types get a lot more interesting when you start using more complex data types for the alternatives. # Bringing them together Let's imagine a very simple language in which you can only give the following instructions: * `move forward X meters` * `rotate Y degrees` A naïve implementation could be: ```scala mdoc final case class Command(label: String, meters: Option[Int], degrees: Option[Int]) ``` This is problematic, however, since it allows so many illegal states to be represented. For example: ```scala mdoc:silent Command("foo", None, None) Command("bar", Some(1), Some(2)) ``` By reworking our type to a slightly more involved ADT, we get rid of these: ```scala mdoc:reset sealed abstract class Command extends Product with Serializable object Command { final case class Move(meters: Int) extends Command final case class Rotate(degrees: Int) extends Command } ``` It's now impossible to create a value that makes no sense - either you move forward by X meters, or you rotate by Y degrees, nothing else. This type also has the advantage of being very pattern match friendly: ```scala mdoc def print(cmd: Command) = cmd match { case Command.Move(dist) => println(s"Moving by ${dist}m") case Command.Rotate(angle) => println(s"Rotating by ${angle}°") } ``` [`Tuple`]:https://www.scala-lang.org/api/2.12.8/scala/Tuple2.html [`Boolean`]:https://www.scala-lang.org/api/2.12.8/scala/Boolean.html ================================================ FILE: src/main/mdoc/_definitions/binary_compatibility.md ================================================ --- title: Binary compatibility layout: definition --- Two versions of a library are said to be binary compatible if you can swap one for the other without recompiling. Let's imagine a `Foo` library such that its `1.0.0` and `1.1.0` versions are binary incompatible (even though they are source compatible). If you have three projects such that: - `A` depends on `Foo 1.0.0` - `B` depends on `Foo 1.1.0` - `C` depends on `A` and `B` Whoever maintains `C` will find himself dealing with some very weird runtime errors that might be a bit a nightmare to work out - that person might not even know that `Foo` exists, let alone that it's in `C`'s dependencies. It's best to strive for binary compatibility for this reason. Tools such as [MiMa](https://github.com/lightbend/migration-manager) have been created to help library author with this task. ================================================ FILE: src/main/mdoc/_definitions/final.md ================================================ --- title: Final types layout: definition --- Marking a type as `final` means that it can never have subtypes. For example, given: ```scala mdoc final class Foo ``` Then the following can't compile: ```scala mdoc:fail class Bar extends Foo ``` ================================================ FILE: src/main/mdoc/_definitions/implicit_scope.md ================================================ --- title: Implicit scope layout: definition --- The implicit scope of a type `T` is where the compiler will look when attempting to locate implicit instances for that type. It is composed of all the companion objects of types associated with `T`. To take a concrete example: ```scala mdoc class Foo object Foo { implicit val bar: List[Foo] = List.empty } ``` `bar` is of type [`List[Foo]`][`List`], and is located within the companion object of `Foo`, a type associated with [`List[Foo]`][`List`]: it's in the implicit scope of [`List[Foo]`][`List`] and we need no special import for the compiler to locate it. ```scala mdoc implicitly[List[Foo]] ``` The implicit scope is particularly helpful when defining [type class](type-class.html) instances. [`List`]:https://www.scala-lang.org/api/2.12.8/scala/collection/immutable/List.html ================================================ FILE: src/main/mdoc/_definitions/paren-less.md ================================================ --- title: Paren-less methods layout: definition --- A paren-less method is a method that doesn't take parameters (a nullary method) and that is declared without parentheses: ```scala mdoc def foo: Int = 1 ``` Trying to call is with parentheses will fail: ```scala mdoc:fail foo() ``` An interesting property of paren-less methods is that they can be overridden (or implemented, if abstract) by `val`s. ================================================ FILE: src/main/mdoc/_definitions/recursion.md ================================================ --- title: Recursion layout: definition --- A recursive function is one that calls itself. For example: ```scala mdoc def sum(is: List[Int]): Int = is match { case h :: t => h + sum(t) case _ => 0 } ``` `sum` is implemented by referencing itself: the sum of a non empty list is the sum of its head plus the sum of its tail. ================================================ FILE: src/main/mdoc/_definitions/reference_equality.md ================================================ --- title: Reference equality layout: definition --- Reference equality is an equality relationship that does not only guarantee that two values are equal, but also that they are, in fact, the same value. The distinction might not be obvious. It's possible to have two values that are equivalent - you can use one or the other to get the exact same result - but not the same. For example: ```scala mdoc:silent val foo = Some(1) val bar = foo val baz = Some(1) ``` `foo`, `bar` and `baz` are all equal: ```scala mdoc foo == bar foo == baz bar == baz ``` On the other hand, while `foo` and `bar` are the same value, `baz` isn't: ```scala mdoc foo eq bar foo eq baz bar eq baz ``` Reference equality is rarely used, except when [warning people against it](../unsafe/array_comparison.html). ================================================ FILE: src/main/mdoc/_definitions/referential_transparency.md ================================================ --- title: Referential transparency layout: definition --- An expression is said to be referentially transparent if it can be replaced by its value and not change the program's behaviour. For example, take the following method: ```scala mdoc def add1(i: Int): Int = i + 1 ``` It's referentially transparent because for any given `i`, you can use `add1(i)` or its result interchangeably. This new version, however, isn't referentially transparent - we call such expressions _referentially opaque_: ```scala mdoc def add1Bis(i: Int): Int = { println(i) i + 1 } ``` If it was referentially transparent, the two following methods would be strictly equivalent: ```scala mdoc def foo1(i: Int): Int = { // We're using the result of add1Bis, twice. val a = add1Bis(i) a + a } def foo2(i: Int): Int = // We're calling add1Bis, twice. add1Bis(i) + add1Bis(i) ``` But they clearly are not: ```scala mdoc foo1(1) foo2(1) ``` # Why is it important? This is more easily explained by showing what you lose without referentialy transparency. ```scala mdoc:silent var i: Int = 1 def addi(j: Int): Int = i + j ``` `addi` is clearly not referentially transparent: it doesn't necessarily return the same value for the same input. Now, let's start 10 threads that'll increment `i`: ```scala mdoc import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global for(_ <- (1 to 10)) scala.concurrent.Future(i += 1) ``` What do you think `addi(1)` evaluates to now? Well, there's no way to know. It could be any number between 1 and 12, depending on how many of our threads have already completed. Let's try: ```scala mdoc addi(1) ``` This uncertainty is something we must strive to avoid at all costs - how do you write correct code if you're not sure what happens when you run it? *This* is what referential transparency gets us: certainty, and understanding of how our code works. # Related terms You'll often hear talk of purity and side effects. Those are just other terms for referentially transparency or opacity: * a function is pure if referentially transparent * a side effect is what makes a function impure ================================================ FILE: src/main/mdoc/_definitions/sealed.md ================================================ --- title: Sealed types layout: definition --- Marking a type as `sealed` means that it can only have direct subtypes that are declared in the same source file. For example, declaring this in, say, `Foo.scala`, will compile: ```scala mdoc sealed class Foo class Bar extends Foo ``` But the following, in `Baz.scala`, will not: ```scala mdoc class Baz extends Foo ``` This is useful when all subtypes of a given type are known and we want to let the compiler know about it - typically, when working with [ADTs](adt.html). ================================================ FILE: src/main/mdoc/_definitions/tail_recursion.md ================================================ --- title: Tail recursion layout: definition --- A tail recursive function is a [recursive] function where the recursive call is the last thing to happen. The following `sum` method is [recursive], but not tail recursive: ```scala mdoc def sum(is: List[Int]): Int = is match { case h :: t => h + sum(t) case _ => 0 } ``` In the first `case`, `sum` calls itself *then* adds the result to the head of the list. To make it tail recursive, we use an accumulator: ```scala mdoc:reset def sum(is: List[Int]): Int = { def loop(cur: List[Int], acc: Int): Int = cur match { case h :: t => loop(t, acc + h) case _ => acc } loop(is, 0) } ``` `sum` itself is not tail recursive - it's not even recursive anymore. On the other hand, `loop` is tail recursive: you can see that its last action is to call itself. [recursive]:./recursion.html ================================================ FILE: src/main/mdoc/_definitions/total_function.md ================================================ --- title: Total functions layout: definition --- A function is said to be total if it's defined for it's entire domain. That is, `A => B` is total if there is a `B` for every possible `A`. A function that is not total is said to be [partial](https://www.scala-lang.org/api/2.12.8/scala/PartialFunction.html). For example, in the following code: ```scala mdoc def int2str(i: Int): String = i.toString def str2int(s: String): Int = Integer.parseInt(s) ``` * `int2str` is total because there exists no [`Int`] for which it's not defined. * `str2int` is partial because there exists *many* [`Strings`][`String`] for which it isn't defined. [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html ================================================ FILE: src/main/mdoc/_definitions/type_class.md ================================================ --- title: Type class layout: definition --- In the context of Scala, a type class is a family of types that support a known set of behaviours. This could be, for example, the family of types that expose a unique integer identifier. This is implemented through a `trait` and any number of implicit instances of that trait. Here's how our _has a unique id_ type class could be represented: ```scala mdoc trait HasId[A] { def getId(a: A): Int } ``` If you manage to get your hands on an instance of `HasId[A]` for a given `A`, you know how to retrieve its id - however this is actually implemented in `A`. Providing instances is done through implicits: ```scala mdoc:silent final case class User(id: Int, name: String) implicit val userHasId: HasId[User] = new HasId[User] { override def getId(user: User) = user.id } ``` Instances are traditionally put in the companion object of either the type class or the type itself - here, either in `HasId` or `User`. Instances declared elsewhere (that are not in the [implicit scope](./implicit_scope.html)) are said to be orphaned. Instead of requiring a subtype of a known type, methods will require an implicit instance of the type class - a type and a proof that it supports the required behaviours. For example: ```scala mdoc def printId[A](a: A)(implicit hia: HasId[A]): Unit = println(s"Found id ${hia.getId(a)}") ``` `printId` isn't hard-coded to a single type, nor to a hierarchy of types with a known root, but to any type that can prove it supports the required behaviour - exposing an identifier. This is a critical aspect of type classes: instances are linked to, but not baked into, types. You can provide instances later, or for types that you don't own. Another important aspect is how type classes compose implicitly. Let's imagine the following: ```scala mdoc trait HasLabel[A] { def getLabel(a: A): String } ``` We could add immediate support for any type that has an `HasId` instance: ```scala mdoc implicit def hasLabelFromId[A](implicit hia: HasId[A]): HasLabel[A] = new HasLabel[A] { override def getLabel(a: A) = s"ID:${hia.getId(a)}" } ``` We can then rewrite our printing function: ```scala mdoc def print[A](a: A)(implicit hla: HasLabel[A]): Unit = println(s"Label: ${hla.getLabel(a)}") ``` And to prove that this all works, let's call it on a `User`, for which we never explicitly provided an instance of `HasLabel`: ```scala mdoc print(User(1, "Foo")) ``` We've essentially just told the compiler that any type that exposed an identifier was also capable of exposing a label. ================================================ FILE: src/main/mdoc/_oop/abstract_fields_as_defs.md ================================================ --- title: Declare abstract fields as paren-less methods layout: article --- > When declaring abstract fields in an abstract class or a trait, it's good practice to declare them as [paren-less] methods. Prefer: ```scala mdoc abstract class Foo { def bar: Int } ``` Over: ```scala mdoc:reset abstract class Foo { val bar: Int } ``` # Reason Scala allows abstract [paren-less] methods to be implemented as `val`s, but not the other way around. Given the following: ```scala mdoc abstract class AsDef { def bar: Int } abstract class AsVal { val bar: Int } ``` Implementing `AsDef.bar` as a `val` compiles: ```scala mdoc:silent new AsDef { override val bar = 0 } ``` But implementing `AsVal.bar` as a `def` does not: ```scala mdoc:fail new AsVal { override def bar = 0 } ``` Declaring abstract fields as `val`s closes some possibilities, while declaring them as [paren-less] methods has no ill effect. # Exceptions to the rule There's at least one known scenario where declaring an abstract field as a `val` is important: path-dependent types, where its important that the field has a concrete type. Here, for example: ```scala mdoc:silent:reset trait Foo { val bar: String } val foo: Foo = new Foo { override val bar = "baz" } ``` It's possible to to refer explicitly to the type of `foo.bar`: ```scala mdoc:silent val fooBar: foo.bar.type = foo.bar ``` Had we defined `bar` as a `def`: ```scala mdoc:reset:silent trait Foo { def bar: String } val foo: Foo = new Foo { override val bar = "baz" } ``` Then we'd get a compilation error attempting to get the type of `foo.bar`: ```scala mdoc:fail val fooBar: foo.bar.type = foo.bar ``` [paren-less]:../definitions/paren-less.html ================================================ FILE: src/main/mdoc/_oop/always_override.md ================================================ --- title: Use override when implementing abstract members layout: article linters: - name: wartremover rules: - name: MissingOverride url: https://github.com/wartremover/wartremover-contrib#missingoverride --- > When overriding a concrete member or implementing an abstract one, use the `override` keyword. # Reason This catches some scenarios where code would otherwise compile but misbehave at runtime. Take the following trait, for example: ```scala mdoc trait Foo[A] { // Default implementation def foo1(a: A): Int = 1 } ``` And now, an incorrect but valid implementation: ```scala mdoc:silent implicit val badFooInt: Foo[Int] = new Foo[Int] { // Notice how this is not quite the right name. def fool(i: Int) = 2 } ``` This compiles, but is clearly not what the we intended: we wanted to implement `fool` but declared `foo1` instead. Since `fool` has a default implementation, this is valid but does not behave the way we think. Had we used the `override` keyword, however, the compiler would have caught our error: ```scala mdoc:fail implicit val fooInt: Foo[Int] = new Foo[Int] { override def fool(i: Int) = 2 } ``` # Alternatives Another accepted practice is to *never* use `override` unless required by the language. This comes from the fact that, for a certain subset of the Scala community, overriding (in the sense of redefining an existing method) is a code smell: the compiler can check that you're working with the right types, but not the invariants that are not expressed in the type system. A common example is implementing a `Set` as a subtype of a `Bag` (see [Subtyping, Subclassing, and Trouble with OOP](http://okmij.org/ftp/Computation/Subtyping/) for an in-depth discussion). Given that premise, the argument is that since overriding is bad, you should treat the use of the `override` keyword like you would an instruction to silence compiler warnings: explicit acknowledgement that you're doing something unsavoury. This debate would be solved by adding a new `implement` keywords, similar to `override` but only used to flag members that you expect to implement abstract members in a superclass. This doesn't exist however, and I feel the pragmatic approach is to follow the rule that catches bad programs rather than the purely ideological one. ================================================ FILE: src/main/mdoc/_oop/index.md ================================================ --- layout: section --- While it should be fairly obvious by now that I lean more on the FP part of the Scala community, the language *does* have a large set of OOP features. When these are the right tools for the job at hand, developers should feel absolutely free to use them. But with care, and if possible while keeping in mind the rules exposed in this section. ================================================ FILE: src/main/mdoc/_partial_functions/either_projection_get.md ================================================ --- title: Do not call get on an Either projection layout: article linters: - name: wartremover rules: - name: EitherProjectionPartial url: http://www.wartremover.org/doc/warts.html#eitherprojectionpartial - name: scapegoat rules: - name: EitherGet --- > When retrieving the content of an [`Either`], do not use [`get`] on one of its projections. # Reason Some projections are [left projections][`LeftProjection`] of a [`Right`], or [right projections][`RightProjection`] of a [`Left`], and [`get`] deals with them by throwing an exception: ```scala mdoc:crash Left(1).right.get ``` ```scala mdoc:crash Right(1).left.get ``` If you have a default value to provide for the "other" case, use [`getOrElse`] on a projection: ```scala mdoc Left(1).right.getOrElse(-1) ``` Alternatively, if you're using the common convention of treating the [`Left`] side of an [`Either`] as the error case, you can also call [`getOrElse`][biasedGetOrElse] directly on the [`Either`]: ```scala mdoc Left(1).getOrElse(-1) ``` Another practical approach is to use [`fold`], which lets you provide a handler for each case: ```scala mdoc (Left(1): Either[Int, Boolean]).fold( i => s"Found an int: '$i'", b => s"Found a boolean: '$b'" ) ``` [`Either`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either.html [`Right`]:https://www.scala-lang.org/api/2.12.8/scala/util/Right.html [`Left`]:https://www.scala-lang.org/api/2.12.8/scala/util/Left.html [`RightProjection`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either$$RightProjection.html [`LeftProjection`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either$$LeftProjection.html [`fold`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either.html#fold[C](fa:A=%3EC,fb:B=%3EC):C [`getOrElse`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either$$RightProjection.html#getOrElse[B1%3E:B](or:=%3EB1):B1 [`get`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html#get:T [biasedGetOrElse]:https://www.scala-lang.org/api/2.12.8/scala/util/Either.html#getOrElse[B1%3E:B](or:=%3EB1):B1 ================================================ FILE: src/main/mdoc/_partial_functions/index.md ================================================ --- layout: section --- Scala, like a lot of other languages, sometimes takes shortcuts in its standard library to meet common expectations. We'll be focusing here on all the [partial functions](../definitions/total_function.html) that are commonly abused and really shouldn't have been there in the first place, and how to avoid them. ================================================ FILE: src/main/mdoc/_partial_functions/option_get.md ================================================ --- title: Do not call get on an Option layout: article linters: - name: wartremover rules: - name: OptionPartial url: http://www.wartremover.org/doc/warts.html#optionpartial - name: scapegoat rules: - name: OptionGet --- > When retrieving the content of an [`Option`], do not use [`get`]. # Reason Some [`Options`][`Option`] are [`Nones`][`None`], and [`get`] deals with them by throwing an exception: ```scala mdoc:crash None.get ``` If you have a default value to provide for the [`None`] case, use [`getOrElse`]: ```scala mdoc None.getOrElse(-1) ``` Another practical approach is to use [`fold`], which lets you provide a handler for each case: ```scala mdoc (None: Option[String]).fold("Found None")(i => s"Found an int: '$i'") ``` [`Option`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html [`None`]:https://www.scala-lang.org/api/2.12.8/scala/None$.html [`get`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html#get:A [`getOrElse`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html#getOrElse[B%3E:A](default:=%3EB):B [`fold`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html#fold[A1%3E:A](z:A1)(op:(A1,A1)=%3EA1):A1 ================================================ FILE: src/main/mdoc/_partial_functions/traversable_head.md ================================================ --- title: Avoid using head layout: article linters: - name: wartremover rules: - name: TraversableOps url: http://www.wartremover.org/doc/warts.html#traversableops - name: scapegoat rules: - name: TraversableHead --- > When retrieving the head of a sequence, use [`headOption`] rather than [`head`]. # Reason Some collections are empty, and [`head`] deals with them by throwing an exception: ```scala mdoc:crash Seq.empty[Int].head ``` [`headOption`] is a safer alternative, since it encodes the possibility of the empty list in its return type: ```scala mdoc Seq(1, 2, 3).headOption Seq.empty[Int].headOption ``` [`head`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#head:A [`headOption`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#headOption:Option[A] ================================================ FILE: src/main/mdoc/_partial_functions/traversable_init.md ================================================ --- title: Avoid using init layout: article linters: - name: wartremover rules: - name: TraversableOps url: http://www.wartremover.org/doc/warts.html#traversableops --- > When retrieving everything but the last element of a sequence, do not use [`init`]. [`dropRight(1)`] is often what you want to use. # Reason Some collections are empty, and [`init`] deals with them by throwing an exception: ```scala mdoc:crash Seq.empty[Int].init ``` [`dropRight(1)`], on the other hand, will yield a reasonable value: the empty list. ```scala mdoc Seq(1, 2, 3).dropRight(1) Seq.empty[Int].dropRight(1) ``` # Exceptions to the rule Note that this is not *always* what you want to do. There are scenarios in which you must deal with the empty case explicitly - as a stop condition in a recursive function, say. It's just that, often, getting the empty list when you ask for everything but the last element of an empty list is perfectly reasonable. [`dropRight(1)`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#dropRight(n:Int):Repr [`init`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#init:Repr ================================================ FILE: src/main/mdoc/_partial_functions/traversable_last.md ================================================ --- title: Avoid using last layout: article linters: - name: wartremover rules: - name: TraversableOps url: http://www.wartremover.org/doc/warts.html#traversableops - name: scapegoat rules: - name: TraversableLast --- > When retrieving the last element of a sequence, use [`lastOption`] rather than [`last`]. # Reason Some collections are empty, and [`last`] deals with them by throwing an exception: ```scala mdoc:crash Seq.empty[Int].last ``` [`lastOption`] is a safer alternative, since it encodes the possibility of the empty list in its return type: ```scala mdoc Seq(1, 2, 3).lastOption Seq.empty[Int].lastOption ``` [`last`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#last:A [`lastOption`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#lastOption:Option[A] ================================================ FILE: src/main/mdoc/_partial_functions/traversable_reduce.md ================================================ --- title: Avoid using reduce layout: article linters: - name: wartremover rules: - name: TraversableOps url: http://www.wartremover.org/doc/warts.html#traversableops --- > When reducing a collection to a single value, prefer [`reduceOption`] to [`reduce`]. # Reason Some collections are empty, and [`reduce`] deals with them by throwing an exception: ```scala mdoc:crash Seq.empty[Int].reduce(_ + _) ``` [`reduceOption`] is a safer alternative, since it encodes the possibility of the empty list in its return type: ```scala mdoc Seq(1, 2, 3).reduceOption(_ + _) Seq.empty[Int].reduceOption(_ + _) ``` [`reduce`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#reduce[A1%3E:A](op:(A1,A1)=%3EA1):A1 [`reduceOption`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#reduceOption[A1%3E:A](op:(A1,A1)=%3EA1):Option[A1] ================================================ FILE: src/main/mdoc/_partial_functions/traversable_tail.md ================================================ --- title: Avoid using tail layout: article linters: - name: wartremover rules: - name: TraversableOps url: http://www.wartremover.org/doc/warts.html#traversableops --- > When retrieving everything but the first element of a sequence, do not use [`tail`]. [`drop(1)`] is often what you want to use. # Reason Some collections are empty, and [`tail`] deals with them by throwing an exception: ```scala mdoc:crash Seq.empty[Int].tail ``` [`drop(1)`], on the other hand, will yield a reasonable value: the empty list. ```scala mdoc Seq(1, 2, 3).drop(1) Seq.empty[Int].drop(1) ``` # Exceptions to the rule Note that this is not *always* what you want to do. There are scenarios in which you must deal with the empty case explicitly - as a stop condition in a recursive function, say. It's just that, often, getting the empty list when you ask for everything but the first element of an empty list is perfectly reasonable. [`tail`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#tail:A [`drop(1)`]:https://www.scala-lang.org/api/2.12.8/scala/collection/Seq.html#drop(n:Int):Repr ================================================ FILE: src/main/mdoc/_partial_functions/try_get.md ================================================ --- title: Do not call get on an Try layout: article linters: - name: wartremover rules: - name: TryPartial url: http://www.wartremover.org/doc/warts.html#trypartial - name: scapegoat rules: - name: TryGet --- > When retrieving the content of a [`Try`], do not use [`get`]. # Reason Some [`Trys`][`Try`] are [`Failures`][`Failure`], and [`get`] deals with them by throwing an exception. ```scala mdoc:crash scala.util.Failure(new Exception).get ``` If you have a default value to provide in case of a [`Failure`], use [`getOrElse`]: ```scala mdoc scala.util.Failure(new Exception).getOrElse(-1) ``` Another practical approach is to use [`fold`], which lets you provide a handler for each case: ```scala mdoc import scala.util.{Failure, Try} (Failure(new Exception): Try[Int]).fold( e => s"Found an error: '${e.getMessage}'", i => s"Found an int: '$i'" ) ``` [`Try`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html [`getOrElse`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html#getOrElse[U%3E:T](default:=%3EU):U [`fold`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html#fold[U](fa:Throwable=%3EU,fb:T=%3EU):U [`get`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html#get:T [`Failure`]:https://www.scala-lang.org/api/2.12.8/scala/util/Failure.html [`Success`]:https://www.scala-lang.org/api/2.12.8/scala/util/Success.html ================================================ FILE: src/main/mdoc/_referential_transparency/avoid_mutability.md ================================================ --- title: Avoid mutability layout: article linters: - name: wartremover rules: - name: Var url: http://www.wartremover.org/doc/warts.html#var - name: MutableDataStructures url: http://www.wartremover.org/doc/warts.html#mutabledatastructures --- > Mutability is almost always a bad idea and should be avoided. # Reasons ## It's referentially opaque Mutability very often breaks [referential transparency][reftrans]. Let's take the following method: ```scala mdoc:silent var i: Int = 0 def foo(j: Int): Int = { i += j i } ``` If it were [referentially transparent][reftrans], it'd always yield the same output for the same input. This is clearly not true: ```scala mdoc foo(1) foo(1) ``` ## It's often premature optimisation Mutability is very often (but not always) used for performance reasons - it's unfortunately been drilled into generations of programmers that instance reuse is the key to fast code. There are a few problems with that assertion, though. First, it's just not really true - immutable data structures, for example, often have the same asymptotic complexity as their mutable counterparts, and are much, much simpler to both implement and work with. Whether or not you believe this to be true, however, is irrelevant, provided you agree that you use mutability to write faster code. If you agree with that, then you agree that mutability is an optimisation - it means the same thing. And optimisations, especially ones that come at such a high cost ([referential opacity][reftrans]), should only be applied when they actually matter, not by default. Optimising by default is called _premature optimisation_ and it's not a good thing. Whenever you feel like using mutability, ask yourself if it's actually worth it. Run benchmarks! If mutability gets you an incredible 50% speedup on a bit of code that's 1% of your runtime, that's a 0.5% speedup overall. Is that really worth the maintenance / complexity cost? # Exceptions to the rule Mutability is acceptable when it doesn't break [referential transparency][reftrans]. The most typical example of that is local mutability: ```scala mdoc def sum(is: List[Int]): Int = { var s = 0 for(i <- is) s += i s } ``` The fact that `s` is mutable isn't exposed to the rest of the world and `sum` is [referentially transparent][reftrans]. If using mutability this way solves some performance bottleneck, go for it! Just for the record though, this example is much cleaner without mutability: ```scala mdoc:reset def sum(is: List[Int]): Int = is.fold(0)(_ + _) ``` [reftrans]:../definitions/referential_transparency.html ================================================ FILE: src/main/mdoc/_referential_transparency/avoid_return.md ================================================ --- title: Do not use return layout: article linters: - name: wartremover rules: - name: Return url: http://www.wartremover.org/doc/warts.html#return - name: scapegoat rules: - name: UnnecessaryReturnUse - name: scalastyle rules: - name: ReturnChecker url: http://www.scalastyle.org/rules-1.0.0.html#org_scalastyle_scalariform_ReturnChecker --- > There's never a good reason to use the `return` keyword. # Reasons ## It makes code hard to reason about There's an argument to be made that the more ways there are to reach a given line of code, the harder that code is to reason about. See [GOTO statements considered harmful] for an eloquent dissertation on the matter. ## It's referentially opaque If `return` were [referentially transparent][reftrans], you should be able to inline expressions that use it and not change the meaning of a program. Here's `foo1`, a method whose return values are pre-computed and stored in values: ```scala mdoc def foo1(i: Int): Int = { val pos = return i val neg = return -i if(i > 0) pos else neg } ``` And `foo2`, the same method but with the return values inlined: ```scala mdoc def foo2(i: Int): Int = { if(i > 0) return i else return -i } ``` If `return` was [referentially transparent][reftrans], we'd get the same `foo1` and `foo2` output for the same input, but: ```scala mdoc foo1(-1) foo2(-1) ``` ## You (probably) don't understand what it does Let's take the following, fairly straightforward method: ```scala mdoc:fail def foo(is: List[Int]): List[Int] = is.map(n => return n + 1) ``` The compilation error doesn't seem to make much sense - how can a [`map`] on a [`List[Int]`][`List`] yield an [`Int`]? But the compiler tends to be smarter than us about these things, so let's play along. ```scala mdoc:fail def foo(is: List[Int]): Int = is.map(n => return n + 1) ``` It's unclear how [`Nothing`] got mixed up in there, and is usually the sign that something went south, but fine, down the rabbit hole we go: ```scala mdoc:fail def foo(is: List[Int]): List[Nothing] = is.map(n => return n + 1) ``` `(╯°□°)╯︵ ┻━┻` I don't understand what's going on there, nor do I really want to. `return` is mad. Don't use it. [`List`]:https://www.scala-lang.org/api/2.12.8/scala/collection/immutable/List.html [`map`]:https://www.scala-lang.org/api/2.12.8/scala/collection/immutable/List.html#max:A [GOTO statements considered harmful]:https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf [`Nothing`]:https://www.scala-lang.org/api/2.12.8/scala/Nothing.html [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html [reftrans]:../definitions/referential_transparency.html ================================================ FILE: src/main/mdoc/_referential_transparency/avoid_throwing_exceptions.md ================================================ --- title: Do not throw exceptions layout: article linters: - name: wartremover rules: - name: Throw url: http://www.wartremover.org/doc/warts.html#throw --- > Do not throw exceptions if you can possibly avoid it. In particular, when: * something might not be there, use [`Option`]. * something might fail, use [`Either`]. * something is rude and might throw an [`Exception`], use [`Try`]. # Reasons ## They're referentially opaque Throwing an [`Exception`] breaks [referential transparency][reftrans]. This can be demonstrated fairly easily. If `throw` was [referentially transparent][reftrans], by definition, the two following methods would be equivalent: ```scala mdoc def foo1() = if(false) throw new Exception else 2 def foo2() = { val a = throw new Exception if (false) a else 2 } ``` Turns out, however, that they aren't. `foo1` terminates: ```scala mdoc foo1() ``` `foo2` fails with an exception: ```scala mdoc:crash foo2() ``` ## They're unsafe Scala uses unchecked exceptions, which means that the compiler is not aware of them, and cannot check whether they're dealt with properly. A function that throws is a bit of a lie: its type implies it's [total function](../definitions/total_function.html) when it's not. Let's take a trivial example: ```scala mdoc def foo(i: Int) = throw new Exception ``` As far as the type checker is concerned, this function is perfectly fine and it'll happily accept the following: ```scala mdoc:crash foo(1) ``` This blows up at runtime, which is really something we'd like to avoid. # Exceptions to the rule It's perfectly fine to throw an exception for truly exceptional errors. CPU not found? Critical hard-drive failure? a required resource hasn't been bundled with the binaries? throw away. But scenarios such as _parse this string into an int_ should never throw - a [`String`] not being an [`Int`] is not exceptional, it's the normal case! There are many more [`String`]s that aren't valid [`Int`]s than the converse. [`Exception`]:https://docs.oracle.com/javase/8/docs/api/java/lang/Exception.html [reftrans]:../definitions/referential_transparency.html [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html [`Option`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html [`Try`]:https://www.scala-lang.org/api/2.12.8/scala/util/Try.html [`Either`]:https://www.scala-lang.org/api/2.12.8/scala/util/Either.html ================================================ FILE: src/main/mdoc/_referential_transparency/index.md ================================================ --- layout: section --- [Referential transparency][reftrans] is a big deal in FP - it's basically the core of what functional programming is. Scala, being the hybrid that it is, has features that are commonly used in imperative languages but break [referential transparency][reftrans] by their very existence. We'll explore the most common (and dangerous) ones here. And for readers that are in the "Scala as an imperative language" part of the community: even then, [referential transparency][reftrans] is a useful property, a good default to strive for but break when needed. [reftrans]:../definitions/referential_transparency.html ================================================ FILE: src/main/mdoc/_tricky_behaviours/final_case_classes.md ================================================ --- title: Mark case classes as final layout: article linters: - name: wartremover rules: - name: FinalCaseClass url: http://www.wartremover.org/doc/warts.html#finalcaseclass - name: scapegoat rules: - name: FinalModifierOnCaseClass --- > When declaring a case class, make it [`final`]. # Reason Extending a case class will produce behaviours that, while perfectly correct, can be surprising and certainly break reasonable expectations. For example: ```scala mdoc case class Foo(i: Int) class Bar(i: Int, s: String) extends Foo(i) ``` `Bar` will behave oddly when it comes to comparison, hashing and printing: ```scala mdoc new Bar(1, "foo") == new Bar(1, "bar") Map( new Bar(1, "foo") -> "foo", new Bar(1, "bar") -> "bar" ) new Bar(1, "baz") ``` This is largely due to Scala's reliance on Java constructs. In this instance: * [`equals`], used to compare two values (and which maps to `==` in Scala) * [`hashCode`], used to compute the hash of a value * [`toString`], used to represent a value as a [`String`] Case classes will generate correct implementations of these methods - [`equals`], for example, will run a field-for-field comparison. The problem is that this breaks when you extend a case class: the subclass will inherit these methods, which are not aware of any field you might have added. In the case of our `Bar` example, the `s` field is ignored by [`equals`], [`toString`] and [`hashCode`], which leads to the surprising (but perfectly correct) behaviour we observed. # Exceptions to the rule There's at least one scenario I know of where a non-[`final`] case class is desirable: when you wish to declare a case class but have absolute control over the values of its fields. Let's say, for example, that you want to create a `PositiveInt` type wrapping an [`Int`] that is guaranteed to be positive. A naïve implementation would be: ```scala mdoc final case class PositiveInt(value: Int) object PositiveInt { def fromInt(i: Int): Option[PositiveInt] = if(i > 0) Some(new PositiveInt(i)) else None } ``` The problem with this implementation is that you still have many ways to create values of type `PositiveInt` that containing a negative [`Int`]: ```scala mdoc new PositiveInt(-1) PositiveInt.fromInt(1).map(_.copy(-1)) ``` In order to seal these holes, a common trick is to declare a `sealed abstract case class`: ```scala mdoc:reset sealed abstract case class PositiveInt(value: Int) object PositiveInt { def fromInt(i: Int): Option[PositiveInt] = if(i > 0) Some(new PositiveInt(i) {}) else None } ``` Since `PositiveInt` is abstract, the compiler can't generate a constructor, a `copy` method or an `apply` method on the companion object and you effectively control the values that get inside it. But this requires it not to be [`final`]. So, as is often the case, there are scenarios in which this rule must be broken. It's a good default behaviour, to be changed when you must. [`equals`]:https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object- [`hashCode`]:https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode-- [`toString`]:https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#toString-- [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html [`final`]:../definitions/final.html [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html ================================================ FILE: src/main/mdoc/_tricky_behaviours/future_in_comprehensions.md ================================================ --- title: Start independent Futures outside of a for-comprehension layout: article --- > When working with independent [`Futures`][`Future`], make sure not to initialise them inside a for-comprehension. # Reason For-comprehension will create a dependency between your [`Futures`][`Future`], turning your code synchronous behind your back. To understand how this can happen, it's important to realise that for-comprehensions are just syntactic sugar for nested [`flatMap`] and [`map`] calls. Take the following code: ```scala mdoc:reset-class import scala.concurrent._ import scala.concurrent.duration._ import ExecutionContext.Implicits.global def longRunning(): Int = { Thread.sleep(100) println("LONG") 1 } def immediate(): Int = { println("IMMEDIATE") 2 } def combine(): Future[Int] = for { i <- Future(longRunning()) j <- Future(immediate()) } yield i + j ``` `combine` desugars to: ```scala mdoc def desugaredCombine(): Future[Int] = Future(longRunning()).flatMap { i => Future(immediate()).map(j => i + j) } ``` This means that the second [`Future`] (the one that yields `2`) cannot be started before the first one has completed - `i` is in its scope, even if not used - even though these two [`Futures`][`Future`] are clearly independent from each other. To make this evident, let's evaluate `combine`. However many times you run it, `LONG` will always be printed before `IMMEDIATE`, even though the former is executed after a significant delay: ```scala mdoc Await.result(combine(), 500.millis) ``` This can be worked around by creating the two [`Future`] instances outside of the for-comprehension: [`Future`] has the controversial behaviour that it starts executing when created, not when evaluated. ```scala mdoc def betterCombine(): Future[Int] = { val f1 = Future(longRunning()) val f2 = Future(immediate()) for { i <- f1 j <- f2 } yield i + j } ``` And if we now evaluate `betterCombine`, the log messages should print in the expected order: ```scala mdoc Await.result(betterCombine(), 200.millis) ``` [`Future`]:https://www.scala-lang.org/api/2.12.8/scala/concurrent/Future.html [`flatMap`]:https://www.scala-lang.org/api/2.12.8/scala/concurrent/Future.html#flatMap[S](f:T=%3Escala.concurrent.Future[S])(implicitexecutor:scala.concurrent.ExecutionContext):scala.concurrent.Future[S] [`map`]:https://www.scala-lang.org/api/2.12.8/scala/concurrent/Future.html#map[S](f:T=%3ES)(implicitexecutor:scala.concurrent.ExecutionContext):scala.concurrent.Future[S] ================================================ FILE: src/main/mdoc/_tricky_behaviours/implicit_shadowing.md ================================================ --- title: Make implicit names as unique as possible layout: article --- > When declaring an implicit value or method, try to give it a name that minimises the chances of conflicting with other libraries. # Reason Implicit names shadow each other and confuse the compiler (and developers). For example: ```scala mdoc object foo { implicit val bar: Int = 1 } object baz { implicit val bar: String = "foo" } ``` Note how both `foo` and `baz` contain a value called `bar`, even though they do not share a type and only one is implicit. This yields the following, confusing behaviour: ```scala mdoc:fail import foo._, baz._ implicitly[Int] ``` There's a single implicit value of type [`Int`] in scope - `foo.bar`. But the compiler can't find it. One way of demonstrating *why* the compiler cannot find it is: ```scala mdoc:fail import foo._, baz._ val a: String = bar ``` Even in the presence of sufficient type information (we know that `a` is a [`String`], and only one of the two `bar`s in scope is a [`String`]), the compiler considers two clashing names to be ambiguous and demands the ambiguity fixed. The problem is easy to understand and fix in this last example - the error message is quite explicit. But in the context of implicit resolution, we're just getting a generic _could not find implicit value_ error message, which can be a bit of a nigthmare to debug. In order to avoid this, and since you cannot control how other libraries name their implicits, you must: * give yours very, very unique names - it doesn't matter if they look silly, they're implicits, the idea is to not refer to them by name * hope that other libraries do the same # Exceptions to the rule There's at least one scenario in which an implicit's name isn't used during resolution: when you declare an implicit of type `T` in the [implicit scope](../definitions/implicit_scope.html) of `T`. For example: ```scala mdoc:reset:silent trait Foo[A] object Foo { implicit val foo: Foo[Int] = new Foo[Int] {} } trait Bar[A] object Bar { implicit val foo: Bar[String] = new Bar[String] {} } ``` Even though both implicit values share the name `foo`, implicit resolution works out: ```scala mdoc // Brings Foo.foo and Bar.foo in scope import Foo._, Bar._ // No conflict here implicitly[Foo[Int]] ``` In this scenario, it's safe to give terse names to your implicits. [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html ================================================ FILE: src/main/mdoc/_tricky_behaviours/index.md ================================================ --- layout: section --- This section explores aspects of Scala that break reasonable expectations or cause cryptic compilation errors. They're easily avoided if you know about them, but have been known to mystify beginners and more advanced developers alike. ================================================ FILE: src/main/mdoc/_tricky_behaviours/leaky_sealed_types.md ================================================ --- title: Make subtypes of sealed types final layout: article linters: - name: wartremover rules: - name: LeakingSealed url: http://www.wartremover.org/doc/warts.html#leakingsealed --- > When writing a subtype for a [`sealed`] type, mark it as [`final`]. # Reason Most people will instinctively assume that a [`sealed`] type cannot have implementations in another source file - that's what a type being [`sealed`] means, right? Well, not quite. A [`sealed`] type can only have *direct* subtypes in the same source file. But the constraint is not transitive and these subtypes, not being [`sealed`] themselves, can have subtypes declared in other files. For example, having this in one file: ```scala mdoc sealed trait Foo class Bar extends Foo ``` We can absolutely extend `Bar` in another source file: ```scala mdoc class FooBar extends Bar ``` This is perfectly correct as per the definition of [`sealed`], and sometimes even desirable. It's just not what most people expect. For this reason, it's preferable to default to [`final`] subtypes unless you have a good reason for them not to be. [`sealed`]:../definitions/sealed.html [`final`]:../definitions/final.html ================================================ FILE: src/main/mdoc/_tricky_behaviours/string_concatenation.md ================================================ --- title: Do not concatenate Strings with + layout: article linters: - name: wartremover rules: - name: StringPlusAny url: http://www.wartremover.org/doc/warts.html#stringplusany --- > When concatenating something to a [`String`], consider string interpolation rather than [`+`]. # Reason Because of a little gem called [`any2stringadd`], [`+`] is just not very sane in Scala. It has, for example, wildly different behaviours for similar collections: ```scala mdoc List("foo") + "bar" Set("foo") + "bar" ``` [`+`] also causes this kind of absolutely nonsensical error message: ```scala mdoc:fail List(1) + 2 ``` This is because of my old nemesis, [implicit conversions](../unsafe/implicit_conversions.html). What the compiler actually ends up trying is: ```scala mdoc:fail:silent any2stringadd(List(1)).+(2) ``` It makes sense when you understand the underlying mechanisms, but good luck explaining that to a beginner. String interpolation, on the other hand, is safe and coherent: ```scala mdoc s"${List("foo")}bar" s"${Set("foo")}bar" s"${List(1)}${2}" ``` [`+`]:https://www.scala-lang.org/api/2.12.8/scala/Any.html#+(other:String):String [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html [`any2stringadd`]:https://www.scala-lang.org/api/2.12.8/scala/Predef$.html#any2stringadd[A]extendsAnyVal ================================================ FILE: src/main/mdoc/_tricky_behaviours/type_implicits.md ================================================ --- title: Add explicit type annotations to implicits layout: article linters: - name: wartremover rules: - name: ExplicitImplicitTypes url: http://www.wartremover.org/doc/warts.html#explicitimplicittypes --- > Always add an explicit type to your implicit values or methods, even when they're private # Reason Aside from [binary compatibility concerns](../binary_compat/explicit_type_annotations.html), implicit resolution is complex and, sometimes, buggy. The following code compiles: ```scala mdoc:silent trait Foo[_] class Bar implicitly[Foo[Bar]] object Bar { implicit val impFoo: Foo[Bar] = new Foo[Bar] {} } ``` `impFoo` is in the [implicit scope](../definitions/implicit_scope.html) of `Foo[Bar]`, and our `implicitly[Foo[Bar]]` resolves. But, because of [bug 8697](https://github.com/scala/bug/issues/8697), the following does not: ```scala mdoc:reset:fail trait Foo[_] class Bar implicitly[Foo[Bar]] object Bar { implicit val impFoo = new Foo[Bar] {} } ``` The only difference between the two is that `impFoo` has an explicit type in the first snippet but not in the second. The behaviour is clearly counter-intuitive, and considered to be a bug - but it results in a compilation failure that's trivial to fix once you know the problem. Well. Most of the time. There are [cases](https://github.com/rickynils/scalacheck/pull/468) where it will result in the *wrong* implicit value being picked, which is a lot harder to realise and fix. ================================================ FILE: src/main/mdoc/_tricky_behaviours/unicode_operators.md ================================================ --- title: Avoid unicode versions of ASCII operators layout: article linters: - name: scalastyle rules: - name: NonASCIICharacterChecker url: http://www.scalastyle.org/rules-1.0.0.html#org_scalastyle_scalariform_NonASCIICharacterChecker --- > ASCII operators (such as `->`) should be preferred over their fancy unicode equivalents (such as `→`). # Reasons ## Not equivalent to their ASCII counterparts While they're supposed to be equivalent, they're not, at least as far as [priority](https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations) is concerned: * `->` has a lower priority than `/` * `→` has a higher priority than `/` This makes the following code legal: ```scala mdoc 1 -> 4 / 2 // Equivalent to 1 -> (4 / 2) ``` The following code, on the other hand, isn't: ```scala mdoc:fail 1 → 4 / 2 // Equivalent to (1 → 4) / 2 ``` This violates reasonable assumptions about the behaviour of unicode operators, and is an accident waiting to happen. Better to avoid them altogether and rely on [fonts with ligature support](https://github.com/tonsky/FiraCode) or [editor support](https://emacsredux.com/blog/2014/08/25/a-peek-at-emacs-24-dot-4-prettify-symbols-mode/) for fancy display. ## Soon to be deprecated As it happens, the feature is controversial enough that it's going to be [deprecated](https://github.com/scala/scala/pull/7540) in an upcoming Scala version. ================================================ FILE: src/main/mdoc/_unsafe/array_comparison.md ================================================ --- title: Do not compare arrays with == layout: article linters: - name: wartremover rules: - name: ArrayEquals url: http://www.wartremover.org/doc/warts.html#arrayequals - name: scapegoat rules: - name: ArrayEquals --- > When comparing two arrays for equality, use [`sameElements`] rather than `==`. # Reason `==` does not compare arrays for value equality, but for [reference equality]. This gives us the following counter-intuitive behaviour: ```scala mdoc Array(1) == Array(1) ``` This is because [`Array`] is essentially an alias for Java's array, which implements [`equals`] as [reference equality] - only returning `true` if two variables point to the same array instance. [`sameElements`], on the other hand, has the behaviour you'd expect: ```scala mdoc Array(1) sameElements Array(1) ``` # Alternatives An alternative to [`sameElements`] is calling [`deep`] on each array before comparison: ```scala mdoc Array(1).deep == Array(1).deep ``` This is not the preferred solution because it's slightly more expensive, creating instances that will be discarded immediately. [`Array`]:https://www.scala-lang.org/api/2.12.8/scala/Array.html [`sameElements`]:https://www.scala-lang.org/api/2.12.8/scala/Array.html#sameElements(that:scala.collection.GenIterable[A]):Boolean [`deep`]:https://www.scala-lang.org/api/2.12.8/scala/Array.html#deep:IndexedSeq[Any] [`equals`]:https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#equals-java.lang.Object-java.lang.Object- [reference equality]:../definitions/reference_equality.html ================================================ FILE: src/main/mdoc/_unsafe/avoid_null.md ================================================ --- title: Do not use null layout: article linters: - name: wartremover rules: - name: "Null" url: http://www.wartremover.org/doc/warts.html#null - name: scapegoat rules: - name: NullAssignment - name: NullParameter - name: scalastyle rules: - name: NullChecker url: http://www.scalastyle.org/rules-1.0.0.html#org_scalastyle_scalariform_NullChecker --- > Whenever `null` seems like a good idea, use [`Option`] instead. # Reasons ## It's not safe As far as types are concerned, `null` is a bit of a lie: ```scala mdoc:silent val s: String = null ``` The compiler believes `s` to be a [`String`] and will accept it wherever one is required. The compiler is, obviously, wrong: ```scala mdoc:crash s.toLowerCase ``` Whenever you're using `null`, you're hindering the compiler's ability to prove your code incorrect. ## It's not pleasant The existence of `null` is terribly unpleasant: it means that you can't trust any value to not be `null`. So, in theory, you'd be forced to write code like: ```scala mdoc def concat(a: String, b: String): String = { if(a == null) b else if(b == null) a else s"$a$b" } ``` That's a lot of boilerplate. In Scala, the general convention is that we pretend `null` does not exist and any value not wrapped in an [`Option`] must always be available. This allows us to rewrite `concat`: ```scala mdoc:reset def concat(a: String, b: String): String = s"$a$b" ``` Just as importantly, if we were to write `concat` such that either `a` or `b` might not be set, the compiler forces us to deal with this: ```scala mdoc:reset def concat(a: Option[String], b: Option[String]): String = s"${a.getOrElse("")}${b.getOrElse("")}" ``` # Exception to the rule There exists at least one scenario in which you must use `null`: Java interop. Java being what it is, some of its APIs use `null` to mean _no value there_, and you don't have much choice but to comply. The standard [`URI`] class, for example, expect `null` values in its constructor - if you don't have a _fragment_, say, stick `null` instead and [`URI`] will work it out. [`Option`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html [`URI`]:https://docs.oracle.com/javase/8/docs/api/java/net/URI.html ================================================ FILE: src/main/mdoc/_unsafe/checking_empty_collection.md ================================================ --- title: Do not compute the size of a collection to check for emptiness layout: article linters: - name: scapegoat rules: - name: AvoidSizeEqualsZero - name: AvoidSizeNotEqualsZero --- > When checking whether a collection is empty, use [`isEmpty`] or [`nonEmpty`] rather than the all too common `size == 0`. # Reasons ## It's inefficient Collections such as [`List`] require a complete traversal to compute their size. Doing so just to check whether there's at least one element is clearly more expensive than it needs to be. To prove this point, let's create a simple benchmarking method: ```scala mdoc def time[A](t: => A): Long = { val now = System.currentTimeMillis() t System.currentTimeMillis() - now } ``` We'll also need an unreasonably large [`List`]: ```scala mdoc:silent val list = (0 to 10000000).toList ``` We can now check that one approach is indeed far longer than the other: ```scala mdoc time(list.isEmpty) time(list.size == 0) ``` ## It's unsafe Some collections are infinite, and will loop forever when you try to compute their length: [//]:I can't use mdoc here, since this will loop forever ```scala // Don't run this. Stream.from(1).size ``` [`isEmpty`], on the other hand, behaves sanely: ```scala mdoc Stream.from(1).isEmpty ``` [`List`]:https://www.scala-lang.org/api/2.12.8/scala/collection/immutable/List.html [`Stream`]:https://www.scala-lang.org/api/2.12.8/scala/collection/immutable/Stream.html [`isEmpty`]:https://www.scala-lang.org/api/2.12.8/scala/collection/SeqLike.html#isEmpty:Boolean [`nonEmpty`]:https://www.scala-lang.org/api/2.12.8/scala/collection/SeqLike.html#nonEmpty:Boolean ================================================ FILE: src/main/mdoc/_unsafe/custom_extractors.md ================================================ --- title: Do not return Option from custom extractors layout: article --- > When defining a custom extractor that happens to be [total](../definitions/total_function.html), always have a return type of [`Some`] rather than [`Option`]. # Reason This is a bit convoluted and has to do with exhaustivity checking in pattern matching, by which I mean: ```scala mdoc:fail def unwrap(oi: Option[Int]): Int = oi match { case Some(i) => i } ``` You get a warning here because there are values of `oi` that would cause your code to fail at runtime. A common misconception about this check is that you get a warning whenever the compiler fails to prove exhaustivity. What actually happens is slightly different: you'll get a warning whenever the compiler succeeds in proving non-exhaustivity. The distinction is subtle, but important: when the compiler cannot find a concrete value that is not covered by a pattern match, even though it knows such a value might exist, it will not warn. And if your custom extractor returns [`Option`], that's exactly what happens: it's telling the compiler that there might be values for which it's not defined, but it doesn't tell it *which* values. This effectively disables exhaustivity checking. Here's a concrete example: ```scala mdoc object ExtractSome { def unapply[A](s: Some[A]): Option[A] = s } ``` Using this code, we can write the following pattern match: ```scala mdoc def unwrap(oi: Option[Int]): Int = oi match { case ExtractSome(i) => i } ``` We're not getting a warning, even though our pattern match is clearly non-exhaustive: it'll fail on [`None`]. ```scala mdoc:crash unwrap(None) ``` The presence of an extractor whose return type is [`Option`] disabled exhaustivity checking, even in the presence of concrete values that are not covered by the pattern match. Let's change our custom extractor to return a [`Some`]: ```scala mdoc:reset object ExtractSome { def unapply[A](s: Some[A]): Some[A] = s } ``` This is a subtle difference that changes everything: [`Some`] means that our extractor will always succeed, allowing the compiler to look for further proof of non-exhaustivity (and find it): ```scala mdoc:fail def unwrap(oi: Option[Int]): Int = oi match { case ExtractSome(i) => i } ``` # Improvement An even better rule of thumb would be to either not define custom extractors, or at least never define partial ones - pattern match exhaustivity is something that most people take for granted, and sneakily disabling it can lead to all sorts of unpleasant runtime behaviours. [`Some`]:https://www.scala-lang.org/api/2.12.8/scala/Some.html [`Option`]:https://www.scala-lang.org/api/2.12.8/scala/Option.html [`None`]:https://www.scala-lang.org/api/2.12.8/scala/None$.html ================================================ FILE: src/main/mdoc/_unsafe/implicit_conversions.md ================================================ --- title: Avoid implicit conversions layout: article linters: - name: wartremover rules: - name: ImplicitConversion url: http://www.wartremover.org/doc/warts.html#implicitconversion --- > Avoid implicit conversions like the plague they are. # Reason Implicit conversions allow the compiler to treat values of a type as values of another type. There's at least one set of scenarios in which this is unambiguously bad: non-[total] conversions. That is, converting an `A` to a `B` when there exists `A`s for which this conversion is impossible. For example, [`String`] to [`Int`]: ```scala mdoc implicit def str2int(str: String): Int = Integer.parseInt(str) ``` What this does is give the compiler a proof, as far as it's concerned, that all [`String`]s are [`Int`]s and he can accept the former where the later is expected. Our proof is unfortunately flawed, and will result in runtime failures: ```scala mdoc:crash "foobar" / 2 ``` # Exceptions to the rule [Total][total] conversions are more palatable - they shouldn't result in runtime failures, at least. One common scenario is adding methods to an existing type: ```scala mdoc implicit class ExtendedInt(i: Int) { def add1: Int = i + 1 } ``` Which lets us run: ```scala mdoc 1.add1 ``` In this scenario, all [`Int`]s can be converted to `ExtendedInt`: the conversion is [total] and cannot result in runtime failure. [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html [`String`]:https://docs.oracle.com/javase/8/docs/api/java/lang/String.html [total]:../definitions/total_function.html ================================================ FILE: src/main/mdoc/_unsafe/index.md ================================================ --- layout: section --- We're now tackling parts of Scala that are actively unsafe to use: misusing them will cause programs not to terminate, or to break in truly incomprehensible ways. Readers should pay attention to these rules, as in my experience even advanced developers sometimes fail to heed some of them. ================================================ FILE: src/main/mdoc/_unsafe/recursion.md ================================================ --- title: Make recursive functions tail-recursive layout: article linters: - name: wartremover rules: - name: Recursion url: http://www.wartremover.org/doc/warts.html#recursion --- > When writing [recursive] functions, consider making them [tail recursive]. # Reason Non-tail recursive functions that call themselves too often end up throwing [`StackOverflowError`] at runtime. Here's a naïve recursive function used to compute the sum of a [`List[Int]`][`List`]: ```scala mdoc def sum(is: List[Int]): Int = is match { case h :: t => h + sum(t) case _ => 0 } ``` This implementation suffers from a major flaw: on long enough lists, it will cause a runtime exception. [//]: I cannot use mdoc here since there's no way I could find to truncate the output, and the stack trace is *large*. ```scala sum(List.range(1, 30000)) // java.lang.StackOverflowError // at .sum(:12) // at .sum(:13) // at .sum(:13) // [...] ``` What happens is that, for each recursive call, the call's context must be stored on the stack - and when a recursive function calls itself a lot, you get a stack overflow exception. When the compiler spots a [tail recursive] function, however, it knows to optimise it by essentially turning it into a `while` loops - no more recursive calls, no more frames pushed on the stack. Here's our `sum` function, but implemented tail recursively: ```scala mdoc:reset def sum(is: List[Int]): Int = { // `loop` i our tail recursive function, keeping a running sum // in `acc` def loop(cur: List[Int], acc: Int): Int = cur match { case h :: t => loop(t, acc + h) case _ => acc } loop(is, 0) } ``` And as you can see, this is now safe: ```scala mdoc sum(List.range(1, 30000)) ``` # Exceptions to the rule Tail recursion is a desirable property, but not an absolute requirement. There are scenarios where "normal" recursion is more appropriate. A fairly standard example is tree traversal: * the recursion is bounded to the height of the tree, so there's no real risk of a [`StackOverflowError`]. * tail-recursive functions for tree exploration are far more complicated than non tail-recursive ones. As is often the case, this rule is more of a default decision, to be overruled when you need to. [recursive]:../definitions/recursion.html [tail recursive]:../definitions/tail_recursion.html [`List`]:https://www.scala-lang.org/api/2.12.8/scala/collection/immutable/List.html [`StackOverflowError`]:https://docs.oracle.com/javase/8/docs/api/java/lang/StackOverflowError.html ================================================ FILE: src/main/mdoc/_unsafe/structural_types.md ================================================ --- title: Avoid structural types layout: article linters: - name: scalastyle rules: - name: StructuralTypeChecker url: http://www.scalastyle.org/rules-1.0.0.html#org_scalastyle_scalariform_StructuralTypeChecker --- > When a structural type seems like a good solution to a problem, consider using a [type class] instead. # Reasons ## Structural types are "slow" Structural types rely on runtime reflection. Since reflection pushes some type work to the runtime, it's inherently slower than solutions where all this work is done at compile time. Note that this is unlikely to be a bottleneck except in very specific use cases, and shouldn't be enough to discourage people in scenarios where structural types are an elegant solution. ## Structural types are not always supported On the other hand, the fact that structural types might fail at runtime for reasons entirely outside of the code author's control probably is a good reason not to use them. When running on the JVM (as opposed to the scala.js or native backend, say), there's one scenario where structural types will break: reflection is controlled by whatever [`SecurityManager`] is running. The default one is quite lenient, but it's possible to configure one that bans reflection altogether. This will break Scala code in ways that are impossible to validate statically. ## Type classes can express the same thing Let's imagine that we want the following behaviour: ```scala mdoc def add1(x: { def get: Int }): Int = 1 + x.get ``` `add1` takes any object that has a `get` method, calls it, adds 1 to it and returns that. For example: ```scala mdoc final case class Wrapper(i: Int) { def get: Int = i } ``` `Wrapper` has a `get` method, and we can call `add1` on it: ```scala mdoc add1(Wrapper(1)) ``` It is, however, possible to express the notion of _has a get: Int_ method as a [type class]: ```scala mdoc trait HasGet[A] { def get(a: A): Int } // add1 now works with any type on which value you can call get def add1[A](a: A)(implicit hga: HasGet[A]): Int = hga.get(a) + 1 ``` Declaring an instance for `Wrapper` is straightforward: ```scala mdoc:silent implicit val wrap: HasGet[Wrapper] = new HasGet[Wrapper] { def get(a: Wrapper) = a.i } ``` And we can now call `add1` like we wanted: ```scala mdoc add1(Wrapper(1)) ``` This solution is admitedly more verbose than the structural type alternative. It's also entirely statically verified, cannot fail at runtime, and is probably also slightly faster. [`SecurityManager`]:https://docs.oracle.com/javase/7/docs/api/java/lang/SecurityManager.html [type class]:../definitions/type_class.html ================================================ FILE: src/main/mdoc/_unsafe/tail_recursion.md ================================================ --- title: Mark tail-recursive functions as such layout: article --- > Always annotate your [tail-recursive] function with [`@annotation.tailrec`]. # Reason This will let the compiler know that you expect your function to be [tail-recursive], and allow compilation to fail should it not be. This is important, because there are scenarios where you'd expect a function to be [tail-recursive] but it really isn't. Take, for example: ```scala mdoc class Foo { def sum(cur: List[Int], acc: Int): Int = cur match { case h :: t => sum(t, acc + h) case _ => acc } } ``` This looks [tail-recursive] - `sum` calls itself in tail position, after all. Turns out, however, that it's not: ```scala mdoc:fail:reset class Foo { @annotation.tailrec def sum(cur: List[Int], acc: Int): Int = cur match { case h :: t => sum(t, acc + h) case _ => acc } } ``` Thanks to that error message, we can fix the problem and make `sum` propery [tail-recursive]: ```scala mdoc:reset class Foo { @annotation.tailrec final def sum(cur: List[Int], acc: Int): Int = cur match { case h :: t => sum(t, acc + h) case _ => acc } } ``` [recursive]:../definitions/recursion.html [tail-recursive]:../definitions/tail_recursion.html [`@annotation.tailrec`]:https://www.scala-lang.org/api/2.12.8/scala/annotation/tailrec.html ================================================ FILE: src/main/mdoc/_warming_up/checking_for_nan.md ================================================ --- title: Use isNaN when checking for NaN layout: article linters: - name: linter rules: - name: UseIsNanNotNanComparison url: https://github.com/HairyFotr/linter/blob/master/src/test/scala/LinterPluginTest.scala#L1930 - name: scapegoat rules: - name: NanComparison --- > When checking whether a number is [`NaN`], use [`isNaN`] rather than `== NaN`. Note that, unlike most rules here, this one is not Scala specific. `NaN` equality behaves unintuitively everywhere `NaN` exists. # Reason By [specification](https://en.wikipedia.org/wiki/IEEE_754), [`NaN`] is not equal to anything, not even itself: ```scala mdoc Double.NaN == Double.NaN ``` Use [`isNaN`] instead, which is meant for just this task: ```scala mdoc Double.NaN.isNaN ``` # Exception to the rule There's one scenario in which you shouldn't use [`isNaN`]: if you're working with a lot of numbers and need to manually validate each one of them. [`isNaN`] can be a bit slow - it's been much improved in 2.12+, but still has a small cost. If you identify this as a bottleneck, you always have the option of being clever. The two following methods produce the same result, but the second one is faster: ```scala mdoc def slowFilterNaN(ds: Seq[Double]): Seq[Double] = ds.filter(d => !d.isNaN) def fastFilterNaN(ds: Seq[Double]): Seq[Double] = ds.filter(d => d == d) ``` Note that this is an optimisation, and one that is sure to mystify some readers. It should only be used when a bottleneck is identified, as the runtime gain is otherwise guaranteed to be lower than the human cost of explaining it over and over again. [`NaN`]:https://www.scala-lang.org/api/2.12.8/scala/Double$.html#NaN:Double(NaN) [`isNaN`]:https://www.scala-lang.org/api/2.12.8/scala/Double.html#isNaN:Boolean ================================================ FILE: src/main/mdoc/_warming_up/checking_for_odd.md ================================================ --- title: Compare remainder to 0 when checking for oddness layout: article linters: - name: scapegoat rules: - name: BrokenOddness --- > When checking whether a number is odd, always use `x % 2 != 0` rather than the more common `x % 2 == 1`. Note that, unlike most rules here, this one is not Scala specific. # Reason There are odd numbers whose division by 2 has a remainder that's not 1: ```scala mdoc -3 % 2 ``` The [mathematical definition](https://en.wikipedia.org/wiki/Parity_(mathematics)) of odd and even numbers is: * an even number is one whose division by 2 has no remainder * an odd number is not even This suggests the following implementations: ```scala mdoc def isEven(i: Int): Boolean = i % 2 == 0 def isOdd(i: Int): Boolean = i % 2 != 0 ``` And, indeed: ```scala mdoc -3 % 2 != 0 ``` ================================================ FILE: src/main/mdoc/_warming_up/index.md ================================================ --- layout: section --- This first section presents a few simple, non-controversial rules, with the purpose of easing the reader into the site's format. Most of these rules are not Scala specific and probably familiar to a lot of readers already, but they should not be skipped - ignoring them caused me an embarrassing number of afternoons spent looking for the cause of behaviours I would have sworn could not possibly happen. ================================================ FILE: src/main/mdoc/_warming_up/numeric_literals.md ================================================ --- title: Use upper case numeric literal suffixes layout: article linters: - name: scalastyle rules: - name: UppercaseLChecker url: http://www.scalastyle.org/rules-1.0.0.html#org_scalastyle_scalariform_UppercaseLChecker --- > When declaring literal numbers such as [`Long`] or [`Float`], use upper case suffix. For example, prefer `2L` to `2l`. # Reason Depending on the font and the syntax highlighting scheme, some letters look a lot like numbers. In some configurations, for example, `l` (lower case `L`) and `1` (one) are basically indistinguishable. Github, for instance, while not the worst offender, isn't doing a great job there. To the casual reader, the following has a fair chance of looking like a list of [`Int`]s, but a [`Long`] sneaked in: ```scala mdoc:silent List(1, 11, 111, 111l, 11111, 11111, 11111) ``` Using upper-case letters makes this more obvious: ```scala mdoc:silent List(1, 11, 111, 111L, 11111, 11111, 11111) ``` [`Long`]:https://www.scala-lang.org/api/2.12.8/scala/Long.html [`Float`]:https://www.scala-lang.org/api/2.12.8/scala/Float.html [`Int`]:https://www.scala-lang.org/api/2.12.8/scala/Int.html ================================================ FILE: src/main/mdoc/index.md ================================================ --- layout: index --- # Foreword I often find myself, both professionaly and otherwise, having to explain bits of Scala to newcomers to the language (but usually not new to programming). Something that's becoming increasingly obvious is that Scala developers follow a certain set of unspoken rules without really thinking about them, and never really explain them to beginners. This is not helping Scala's reputation as a hard to learn language, which is unfortunate - it's a language I enjoy and wish more people would learn. This site is meant to address that, by listing and explaining all these rules I wish someone'd told me about when I was learning the language. Comments, suggestions, ... are welcome! I'm clearly not the best Scala developer out there and might have gotten things wrong, or right for the wrong reasons. If you see areas that could be improved on, feel free to open an issue! ================================================ FILE: src/site/_config.yml ================================================ name: Scala Best Practices markdown: kramdown highlighter: rouge baseurl: /scala-best-practices collections: warming_up: output: true title: Warming up sequence: 1 tricky_behaviours: output: true title: Tricky behaviours sequence: 2 unsafe: output: true title: Unsafe patterns sequence: 3 partial_functions: output: true title: Partial functions sequence: 4 binary_compat: output: true title: Binary compatibility sequence: 5 referential_transparency: output: true title: Referential transparency sequence: 6 adts: output: true title: ADTs sequence: 7 oop: output: true title: OOP sequence: 8 definitions: output: true kramdown: input: GFM hard_wrap: false compress_html: clippings: all comments: [""] endings: all ================================================ FILE: src/site/_includes/footer.html ================================================ ================================================ FILE: src/site/_includes/header.html ================================================ {{ site.name }}{% if page.title %} - {{ page.title }}{% endif %} ================================================ FILE: src/site/_includes/linters.html ================================================

Checked by

{% if include.linters %} {% assign sorted_linters = include.linters | sort: "name" %} {% for linter in sorted_linters %} {% case linter.name %} {% when 'wartremover' %} {% assign linter_name = 'WartRemover' %} {% assign linter_url = 'http://www.wartremover.org/' %} {% when 'scapegoat' %} {% assign linter_name = 'Scapegoat' %} {% assign linter_url = 'https://github.com/sksamuel/scapegoat' %} {% when 'linter' %} {% assign linter_name = 'Scala Linter' %} {% assign linter_url = 'https://github.com/HairyFotr/linter' %} {% when 'scalastyle' %} {% assign linter_name = 'Scalastyle' %} {% assign linter_url = 'http://www.scalastyle.org/' %} {% else %} {% assign linter_url = false %} {% endcase %} {% endfor %}
Linter Rule
{% if linter_url %} {{ linter_name }} {% else %} {{ linter_name }} {% endif %}
    {% for rule in linter.rules %}
  • {% if rule.url %} {{ rule.name }} {% else %} {{ rule.name }} {% endif %}
  • {% endfor %}
{% else %} This rule is not checked by any linter that I know of. {% endif %} ================================================ FILE: src/site/_includes/toc.html ================================================ {% assign sorted_pages = include.collection | sort: "title" %}
    {% for x in sorted_pages %} {% if x.title != "Index" %}
  • {% if x.status == "wip" %} {{ x.title }} (WIP) {% else %} {{ x.title }} {% endif %}
  • {% endif %} {% endfor %}
================================================ FILE: src/site/_layouts/article.html ================================================ --- layout: compress --- {% include header.html %}
{% if page.title %}

{{ page.title }}

{% endif %} {{ content }} {% include linters.html linters=page.linters %}
================================================ FILE: src/site/_layouts/compress.html ================================================ --- # Jekyll layout that compresses HTML # v3.0.4 # http://jch.penibelst.de/ # © 2014–2015 Anatol Broder # MIT License --- {% capture _LINE_FEED %} {% endcapture %}{% if site.compress_html.ignore.envs contains jekyll.environment %}{{ content }}{% else %}{% capture _content %}{{ content }}{% endcapture %}{% assign _profile = site.compress_html.profile %}{% if site.compress_html.endings == "all" %}{% assign _endings = "html head body li dt dd optgroup option colgroup caption thead tbody tfoot tr td th" | split: " " %}{% else %}{% assign _endings = site.compress_html.endings %}{% endif %}{% for _element in _endings %}{% capture _end %}{% endcapture %}{% assign _content = _content | remove: _end %}{% endfor %}{% if _profile and _endings %}{% assign _profile_endings = _content | size | plus: 1 %}{% endif %}{% for _element in site.compress_html.startings %}{% capture _start %}<{{ _element }}>{% endcapture %}{% assign _content = _content | remove: _start %}{% endfor %}{% if _profile and site.compress_html.startings %}{% assign _profile_startings = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.comments == "all" %}{% assign _comments = "" | split: " " %}{% else %}{% assign _comments = site.compress_html.comments %}{% endif %}{% if _comments.size == 2 %}{% capture _comment_befores %}.{{ _content }}{% endcapture %}{% assign _comment_befores = _comment_befores | split: _comments.first %}{% for _comment_before in _comment_befores %}{% if forloop.first %}{% continue %}{% endif %}{% capture _comment_outside %}{% if _carry %}{{ _comments.first }}{% endif %}{{ _comment_before }}{% endcapture %}{% capture _comment %}{% unless _carry %}{{ _comments.first }}{% endunless %}{{ _comment_outside | split: _comments.last | first }}{% if _comment_outside contains _comments.last %}{{ _comments.last }}{% assign _carry = false %}{% else %}{% assign _carry = true %}{% endif %}{% endcapture %}{% assign _content = _content | remove_first: _comment %}{% endfor %}{% if _profile %}{% assign _profile_comments = _content | size | plus: 1 %}{% endif %}{% endif %}{% assign _pre_befores = _content | split: "" %}{% assign _pres_after = "" %}{% if _pres.size != 0 %}{% if site.compress_html.blanklines %}{% assign _lines = _pres.last | split: _LINE_FEED %}{% capture _pres_after %}{% for _line in _lines %}{% assign _trimmed = _line | split: " " | join: " " %}{% if _trimmed != empty or forloop.last %}{% unless forloop.first %}{{ _LINE_FEED }}{% endunless %}{{ _line }}{% endif %}{% endfor %}{% endcapture %}{% else %}{% assign _pres_after = _pres.last | split: " " | join: " " %}{% endif %}{% endif %}{% capture _content %}{{ _content }}{% if _pre_before contains "" %}{% endif %}{% unless _pre_before contains "" and _pres.size == 1 %}{{ _pres_after }}{% endunless %}{% endcapture %}{% endfor %}{% if _profile %}{% assign _profile_collapse = _content | size | plus: 1 %}{% endif %}{% if site.compress_html.clippings == "all" %}{% assign _clippings = "html head title base link meta style body article section nav aside h1 h2 h3 h4 h5 h6 hgroup header footer address p hr blockquote ol ul li dl dt dd figure figcaption main div table caption colgroup col tbody thead tfoot tr td th" | split: " " %}{% else %}{% assign _clippings = site.compress_html.clippings %}{% endif %}{% for _element in _clippings %}{% assign _edges = " ;; ;" | replace: "e", _element | split: ";" %}{% assign _content = _content | replace: _edges[0], _edges[1] | replace: _edges[2], _edges[3] | replace: _edges[4], _edges[5] %}{% endfor %}{% if _profile and _clippings %}{% assign _profile_clippings = _content | size | plus: 1 %}{% endif %}{{ _content }}{% if _profile %}
Step Bytes
raw {{ content | size }}{% if _profile_endings %}
endings {{ _profile_endings }}{% endif %}{% if _profile_startings %}
startings {{ _profile_startings }}{% endif %}{% if _profile_comments %}
comments {{ _profile_comments }}{% endif %}{% if _profile_collapse %}
collapse {{ _profile_collapse }}{% endif %}{% if _profile_clippings %}
clippings {{ _profile_clippings }}{% endif %}
{% endif %}{% endif %} ================================================ FILE: src/site/_layouts/definition.html ================================================ --- layout: compress --- {% include header.html %}
{% if page.title %}

{{ page.title }}

{% endif %} {{ content }}
================================================ FILE: src/site/_layouts/index.html ================================================ --- layout: compress --- {% include header.html %}
{{ content }}

Table of Contents

{% assign cs = site.collections | sort: 'sequence' %} {% for c in cs %} {% if c.title %}

{{ c.title }}

{% include toc.html collection=c.docs %} {% endif %} {% endfor %}
================================================ FILE: src/site/_layouts/section.html ================================================ --- layout: compress --- {% include header.html %} {% for c in site.collections %} {% if c.label == page.collection %} {% assign col = c %} {% endif %} {% endfor %}

{{ col.title }}

{{ content }}

Content

{% include toc.html collection=col.docs %}
================================================ FILE: src/site/css/fira_code/fira_code.css ================================================ @font-face{ font-family: 'Fira Code'; src: url('woff2/FiraCode-Light.woff2') format('woff2'), url('woff/FiraCode-Light.woff') format('woff'); font-weight: 300; font-style: normal; } @font-face{ font-family: 'Fira Code'; src: url('woff2/FiraCode-Regular.woff2') format('woff2'), url('woff/FiraCode-Regular.woff') format('woff'); font-weight: 400; font-style: normal; } @font-face{ font-family: 'Fira Code'; src: url('woff2/FiraCode-Medium.woff2') format('woff2'), url('woff/FiraCode-Medium.woff') format('woff'); font-weight: 500; font-style: normal; } @font-face{ font-family: 'Fira Code'; src: url('woff2/FiraCode-Bold.woff2') format('woff2'), url('woff/FiraCode-Bold.woff') format('woff'); font-weight: 700; font-style: normal; } ================================================ FILE: src/site/css/fontawesome/font-awesome.css ================================================ /*! * Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * Copyright 2022 Fonticons, Inc. */ .fa { font-family: var(--fa-style-family, "Font Awesome 6 Free"); font-weight: var(--fa-style, 900) } .fa, .fa-brands, .fa-classic, .fa-regular, .fa-sharp, .fa-solid, .fab, .far, .fas { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; display: var(--fa-display, inline-block); font-style: normal; font-variant: normal; line-height: 1; text-rendering: auto } .fa-classic, .fa-regular, .fa-solid, .far, .fas { font-family: "Font Awesome 6 Free" } .fa-brands, .fab { font-family: "Font Awesome 6 Brands" } .fa-1x { font-size: 1em } .fa-2x { font-size: 2em } .fa-3x { font-size: 3em } .fa-4x { font-size: 4em } .fa-5x { font-size: 5em } .fa-6x { font-size: 6em } .fa-7x { font-size: 7em } .fa-8x { font-size: 8em } .fa-9x { font-size: 9em } .fa-10x { font-size: 10em } .fa-2xs { font-size: .625em; line-height: .1em; vertical-align: .225em } .fa-xs { font-size: .75em; line-height: .08333em; vertical-align: .125em } .fa-sm { font-size: .875em; line-height: .07143em; vertical-align: .05357em } .fa-lg { font-size: 1.25em; line-height: .05em; vertical-align: -.075em } .fa-xl { font-size: 1.5em; line-height: .04167em; vertical-align: -.125em } .fa-2xl { font-size: 2em; line-height: .03125em; vertical-align: -.1875em } .fa-fw { text-align: center; width: 1.25em } .fa-ul { list-style-type: none; margin-left: var(--fa-li-margin, 2.5em); padding-left: 0 } .fa-ul>li { position: relative } .fa-li { left: calc(var(--fa-li-width, 2em)*-1); position: absolute; text-align: center; width: var(--fa-li-width, 2em); line-height: inherit } .fa-border { border-radius: var(--fa-border-radius, .1em); border: var(--fa-border-width, .08em) var(--fa-border-style, solid) var(--fa-border-color, #eee); padding: var(--fa-border-padding, .2em .25em .15em) } .fa-pull-left { float: left; margin-right: var(--fa-pull-margin, .3em) } .fa-pull-right { float: right; margin-left: var(--fa-pull-margin, .3em) } .fa-beat { -webkit-animation-name: fa-beat; animation-name: fa-beat; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); animation-timing-function: var(--fa-animation-timing, ease-in-out) } .fa-bounce { -webkit-animation-name: fa-bounce; animation-name: fa-bounce; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(.28, .84, .42, 1)); animation-timing-function: var(--fa-animation-timing, cubic-bezier(.28, .84, .42, 1)) } .fa-fade { -webkit-animation-name: fa-fade; animation-name: fa-fade; -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1)); animation-timing-function: var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1)) } .fa-beat-fade, .fa-fade { -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s) } .fa-beat-fade { -webkit-animation-name: fa-beat-fade; animation-name: fa-beat-fade; -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1)); animation-timing-function: var(--fa-animation-timing, cubic-bezier(.4, 0, .6, 1)) } .fa-flip { -webkit-animation-name: fa-flip; animation-name: fa-flip; -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); animation-timing-function: var(--fa-animation-timing, ease-in-out) } .fa-shake { -webkit-animation-name: fa-shake; animation-name: fa-shake; -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, linear); animation-timing-function: var(--fa-animation-timing, linear) } .fa-shake, .fa-spin { -webkit-animation-delay: var(--fa-animation-delay, 0s); animation-delay: var(--fa-animation-delay, 0s); -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal) } .fa-spin { -webkit-animation-name: fa-spin; animation-name: fa-spin; -webkit-animation-duration: var(--fa-animation-duration, 2s); animation-duration: var(--fa-animation-duration, 2s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, linear); animation-timing-function: var(--fa-animation-timing, linear) } .fa-spin-reverse { --fa-animation-direction: reverse } .fa-pulse, .fa-spin-pulse { -webkit-animation-name: fa-spin; animation-name: fa-spin; -webkit-animation-direction: var(--fa-animation-direction, normal); animation-direction: var(--fa-animation-direction, normal); -webkit-animation-duration: var(--fa-animation-duration, 1s); animation-duration: var(--fa-animation-duration, 1s); -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); animation-iteration-count: var(--fa-animation-iteration-count, infinite); -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); animation-timing-function: var(--fa-animation-timing, steps(8)) } @media (prefers-reduced-motion:reduce) { .fa-beat, .fa-beat-fade, .fa-bounce, .fa-fade, .fa-flip, .fa-pulse, .fa-shake, .fa-spin, .fa-spin-pulse { -webkit-animation-delay: -1ms; animation-delay: -1ms; -webkit-animation-duration: 1ms; animation-duration: 1ms; -webkit-animation-iteration-count: 1; animation-iteration-count: 1; transition-delay: 0s; transition-duration: 0s } } @-webkit-keyframes fa-beat { 0%, 90% { -webkit-transform: scale(1); transform: scale(1) } 45% { -webkit-transform: scale(var(--fa-beat-scale, 1.25)); transform: scale(var(--fa-beat-scale, 1.25)) } } @keyframes fa-beat { 0%, 90% { -webkit-transform: scale(1); transform: scale(1) } 45% { -webkit-transform: scale(var(--fa-beat-scale, 1.25)); transform: scale(var(--fa-beat-scale, 1.25)) } } @-webkit-keyframes fa-bounce { 0% { -webkit-transform: scale(1) translateY(0); transform: scale(1) translateY(0) } 10% { -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, .9)) translateY(0); transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, .9)) translateY(0) } 30% { -webkit-transform: scale(var(--fa-bounce-jump-scale-x, .9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -.5em)); transform: scale(var(--fa-bounce-jump-scale-x, .9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -.5em)) } 50% { -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, .95)) translateY(0); transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, .95)) translateY(0) } 57% { -webkit-transform: scale(1) translateY(var(--fa-bounce-rebound, -.125em)); transform: scale(1) translateY(var(--fa-bounce-rebound, -.125em)) } 64% { -webkit-transform: scale(1) translateY(0); transform: scale(1) translateY(0) } to { -webkit-transform: scale(1) translateY(0); transform: scale(1) translateY(0) } } @keyframes fa-bounce { 0% { -webkit-transform: scale(1) translateY(0); transform: scale(1) translateY(0) } 10% { -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, .9)) translateY(0); transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, .9)) translateY(0) } 30% { -webkit-transform: scale(var(--fa-bounce-jump-scale-x, .9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -.5em)); transform: scale(var(--fa-bounce-jump-scale-x, .9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -.5em)) } 50% { -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, .95)) translateY(0); transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, .95)) translateY(0) } 57% { -webkit-transform: scale(1) translateY(var(--fa-bounce-rebound, -.125em)); transform: scale(1) translateY(var(--fa-bounce-rebound, -.125em)) } 64% { -webkit-transform: scale(1) translateY(0); transform: scale(1) translateY(0) } to { -webkit-transform: scale(1) translateY(0); transform: scale(1) translateY(0) } } @-webkit-keyframes fa-fade { 50% { opacity: var(--fa-fade-opacity, .4) } } @keyframes fa-fade { 50% { opacity: var(--fa-fade-opacity, .4) } } @-webkit-keyframes fa-beat-fade { 0%, to { opacity: var(--fa-beat-fade-opacity, .4); -webkit-transform: scale(1); transform: scale(1) } 50% { opacity: 1; -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); transform: scale(var(--fa-beat-fade-scale, 1.125)) } } @keyframes fa-beat-fade { 0%, to { opacity: var(--fa-beat-fade-opacity, .4); -webkit-transform: scale(1); transform: scale(1) } 50% { opacity: 1; -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); transform: scale(var(--fa-beat-fade-scale, 1.125)) } } @-webkit-keyframes fa-flip { 50% { -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)) } } @keyframes fa-flip { 50% { -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)) } } @-webkit-keyframes fa-shake { 0% { -webkit-transform: rotate(-15deg); transform: rotate(-15deg) } 4% { -webkit-transform: rotate(15deg); transform: rotate(15deg) } 8%, 24% { -webkit-transform: rotate(-18deg); transform: rotate(-18deg) } 12%, 28% { -webkit-transform: rotate(18deg); transform: rotate(18deg) } 16% { -webkit-transform: rotate(-22deg); transform: rotate(-22deg) } 20% { -webkit-transform: rotate(22deg); transform: rotate(22deg) } 32% { -webkit-transform: rotate(-12deg); transform: rotate(-12deg) } 36% { -webkit-transform: rotate(12deg); transform: rotate(12deg) } 40%, to { -webkit-transform: rotate(0deg); transform: rotate(0deg) } } @keyframes fa-shake { 0% { -webkit-transform: rotate(-15deg); transform: rotate(-15deg) } 4% { -webkit-transform: rotate(15deg); transform: rotate(15deg) } 8%, 24% { -webkit-transform: rotate(-18deg); transform: rotate(-18deg) } 12%, 28% { -webkit-transform: rotate(18deg); transform: rotate(18deg) } 16% { -webkit-transform: rotate(-22deg); transform: rotate(-22deg) } 20% { -webkit-transform: rotate(22deg); transform: rotate(22deg) } 32% { -webkit-transform: rotate(-12deg); transform: rotate(-12deg) } 36% { -webkit-transform: rotate(12deg); transform: rotate(12deg) } 40%, to { -webkit-transform: rotate(0deg); transform: rotate(0deg) } } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg) } to { -webkit-transform: rotate(1turn); transform: rotate(1turn) } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg) } to { -webkit-transform: rotate(1turn); transform: rotate(1turn) } } .fa-rotate-90 { -webkit-transform: rotate(90deg); transform: rotate(90deg) } .fa-rotate-180 { -webkit-transform: rotate(180deg); transform: rotate(180deg) } .fa-rotate-270 { -webkit-transform: rotate(270deg); transform: rotate(270deg) } .fa-flip-horizontal { -webkit-transform: scaleX(-1); transform: scaleX(-1) } .fa-flip-vertical { -webkit-transform: scaleY(-1); transform: scaleY(-1) } .fa-flip-both, .fa-flip-horizontal.fa-flip-vertical { -webkit-transform: scale(-1); transform: scale(-1) } .fa-rotate-by { -webkit-transform: rotate(var(--fa-rotate-angle, none)); transform: rotate(var(--fa-rotate-angle, none)) } .fa-stack { display: inline-block; height: 2em; line-height: 2em; position: relative; vertical-align: middle; width: 2.5em } .fa-stack-1x, .fa-stack-2x { left: 0; position: absolute; text-align: center; width: 100%; z-index: var(--fa-stack-z-index, auto) } .fa-stack-1x { line-height: inherit } .fa-stack-2x { font-size: 2em } .fa-inverse { color: var(--fa-inverse, #fff) } .fa-0:before { content: "\30" } .fa-1:before { content: "\31" } .fa-2:before { content: "\32" } .fa-3:before { content: "\33" } .fa-4:before { content: "\34" } .fa-5:before { content: "\35" } .fa-6:before { content: "\36" } .fa-7:before { content: "\37" } .fa-8:before { content: "\38" } .fa-9:before { content: "\39" } .fa-fill-drip:before { content: "\f576" } .fa-arrows-to-circle:before { content: "\e4bd" } .fa-chevron-circle-right:before, .fa-circle-chevron-right:before { content: "\f138" } .fa-at:before { content: "\40" } .fa-trash-alt:before, .fa-trash-can:before { content: "\f2ed" } .fa-text-height:before { content: "\f034" } .fa-user-times:before, .fa-user-xmark:before { content: "\f235" } .fa-stethoscope:before { content: "\f0f1" } .fa-comment-alt:before, .fa-message:before { content: "\f27a" } .fa-info:before { content: "\f129" } .fa-compress-alt:before, .fa-down-left-and-up-right-to-center:before { content: "\f422" } .fa-explosion:before { content: "\e4e9" } .fa-file-alt:before, .fa-file-lines:before, .fa-file-text:before { content: "\f15c" } .fa-wave-square:before { content: "\f83e" } .fa-ring:before { content: "\f70b" } .fa-building-un:before { content: "\e4d9" } .fa-dice-three:before { content: "\f527" } .fa-calendar-alt:before, .fa-calendar-days:before { content: "\f073" } .fa-anchor-circle-check:before { content: "\e4aa" } .fa-building-circle-arrow-right:before { content: "\e4d1" } .fa-volleyball-ball:before, .fa-volleyball:before { content: "\f45f" } .fa-arrows-up-to-line:before { content: "\e4c2" } .fa-sort-desc:before, .fa-sort-down:before { content: "\f0dd" } .fa-circle-minus:before, .fa-minus-circle:before { content: "\f056" } .fa-door-open:before { content: "\f52b" } .fa-right-from-bracket:before, .fa-sign-out-alt:before { content: "\f2f5" } .fa-atom:before { content: "\f5d2" } .fa-soap:before { content: "\e06e" } .fa-heart-music-camera-bolt:before, .fa-icons:before { content: "\f86d" } .fa-microphone-alt-slash:before, .fa-microphone-lines-slash:before { content: "\f539" } .fa-bridge-circle-check:before { content: "\e4c9" } .fa-pump-medical:before { content: "\e06a" } .fa-fingerprint:before { content: "\f577" } .fa-hand-point-right:before { content: "\f0a4" } .fa-magnifying-glass-location:before, .fa-search-location:before { content: "\f689" } .fa-forward-step:before, .fa-step-forward:before { content: "\f051" } .fa-face-smile-beam:before, .fa-smile-beam:before { content: "\f5b8" } .fa-flag-checkered:before { content: "\f11e" } .fa-football-ball:before, .fa-football:before { content: "\f44e" } .fa-school-circle-exclamation:before { content: "\e56c" } .fa-crop:before { content: "\f125" } .fa-angle-double-down:before, .fa-angles-down:before { content: "\f103" } .fa-users-rectangle:before { content: "\e594" } .fa-people-roof:before { content: "\e537" } .fa-people-line:before { content: "\e534" } .fa-beer-mug-empty:before, .fa-beer:before { content: "\f0fc" } .fa-diagram-predecessor:before { content: "\e477" } .fa-arrow-up-long:before, .fa-long-arrow-up:before { content: "\f176" } .fa-burn:before, .fa-fire-flame-simple:before { content: "\f46a" } .fa-male:before, .fa-person:before { content: "\f183" } .fa-laptop:before { content: "\f109" } .fa-file-csv:before { content: "\f6dd" } .fa-menorah:before { content: "\f676" } .fa-truck-plane:before { content: "\e58f" } .fa-record-vinyl:before { content: "\f8d9" } .fa-face-grin-stars:before, .fa-grin-stars:before { content: "\f587" } .fa-bong:before { content: "\f55c" } .fa-pastafarianism:before, .fa-spaghetti-monster-flying:before { content: "\f67b" } .fa-arrow-down-up-across-line:before { content: "\e4af" } .fa-spoon:before, .fa-utensil-spoon:before { content: "\f2e5" } .fa-jar-wheat:before { content: "\e517" } .fa-envelopes-bulk:before, .fa-mail-bulk:before { content: "\f674" } .fa-file-circle-exclamation:before { content: "\e4eb" } .fa-circle-h:before, .fa-hospital-symbol:before { content: "\f47e" } .fa-pager:before { content: "\f815" } .fa-address-book:before, .fa-contact-book:before { content: "\f2b9" } .fa-strikethrough:before { content: "\f0cc" } .fa-k:before { content: "\4b" } .fa-landmark-flag:before { content: "\e51c" } .fa-pencil-alt:before, .fa-pencil:before { content: "\f303" } .fa-backward:before { content: "\f04a" } .fa-caret-right:before { content: "\f0da" } .fa-comments:before { content: "\f086" } .fa-file-clipboard:before, .fa-paste:before { content: "\f0ea" } .fa-code-pull-request:before { content: "\e13c" } .fa-clipboard-list:before { content: "\f46d" } .fa-truck-loading:before, .fa-truck-ramp-box:before { content: "\f4de" } .fa-user-check:before { content: "\f4fc" } .fa-vial-virus:before { content: "\e597" } .fa-sheet-plastic:before { content: "\e571" } .fa-blog:before { content: "\f781" } .fa-user-ninja:before { content: "\f504" } .fa-person-arrow-up-from-line:before { content: "\e539" } .fa-scroll-torah:before, .fa-torah:before { content: "\f6a0" } .fa-broom-ball:before, .fa-quidditch-broom-ball:before, .fa-quidditch:before { content: "\f458" } .fa-toggle-off:before { content: "\f204" } .fa-archive:before, .fa-box-archive:before { content: "\f187" } .fa-person-drowning:before { content: "\e545" } .fa-arrow-down-9-1:before, .fa-sort-numeric-desc:before, .fa-sort-numeric-down-alt:before { content: "\f886" } .fa-face-grin-tongue-squint:before, .fa-grin-tongue-squint:before { content: "\f58a" } .fa-spray-can:before { content: "\f5bd" } .fa-truck-monster:before { content: "\f63b" } .fa-w:before { content: "\57" } .fa-earth-africa:before, .fa-globe-africa:before { content: "\f57c" } .fa-rainbow:before { content: "\f75b" } .fa-circle-notch:before { content: "\f1ce" } .fa-tablet-alt:before, .fa-tablet-screen-button:before { content: "\f3fa" } .fa-paw:before { content: "\f1b0" } .fa-cloud:before { content: "\f0c2" } .fa-trowel-bricks:before { content: "\e58a" } .fa-face-flushed:before, .fa-flushed:before { content: "\f579" } .fa-hospital-user:before { content: "\f80d" } .fa-tent-arrow-left-right:before { content: "\e57f" } .fa-gavel:before, .fa-legal:before { content: "\f0e3" } .fa-binoculars:before { content: "\f1e5" } .fa-microphone-slash:before { content: "\f131" } .fa-box-tissue:before { content: "\e05b" } .fa-motorcycle:before { content: "\f21c" } .fa-bell-concierge:before, .fa-concierge-bell:before { content: "\f562" } .fa-pen-ruler:before, .fa-pencil-ruler:before { content: "\f5ae" } .fa-people-arrows-left-right:before, .fa-people-arrows:before { content: "\e068" } .fa-mars-and-venus-burst:before { content: "\e523" } .fa-caret-square-right:before, .fa-square-caret-right:before { content: "\f152" } .fa-cut:before, .fa-scissors:before { content: "\f0c4" } .fa-sun-plant-wilt:before { content: "\e57a" } .fa-toilets-portable:before { content: "\e584" } .fa-hockey-puck:before { content: "\f453" } .fa-table:before { content: "\f0ce" } .fa-magnifying-glass-arrow-right:before { content: "\e521" } .fa-digital-tachograph:before, .fa-tachograph-digital:before { content: "\f566" } .fa-users-slash:before { content: "\e073" } .fa-clover:before { content: "\e139" } .fa-mail-reply:before, .fa-reply:before { content: "\f3e5" } .fa-star-and-crescent:before { content: "\f699" } .fa-house-fire:before { content: "\e50c" } .fa-minus-square:before, .fa-square-minus:before { content: "\f146" } .fa-helicopter:before { content: "\f533" } .fa-compass:before { content: "\f14e" } .fa-caret-square-down:before, .fa-square-caret-down:before { content: "\f150" } .fa-file-circle-question:before { content: "\e4ef" } .fa-laptop-code:before { content: "\f5fc" } .fa-swatchbook:before { content: "\f5c3" } .fa-prescription-bottle:before { content: "\f485" } .fa-bars:before, .fa-navicon:before { content: "\f0c9" } .fa-people-group:before { content: "\e533" } .fa-hourglass-3:before, .fa-hourglass-end:before { content: "\f253" } .fa-heart-broken:before, .fa-heart-crack:before { content: "\f7a9" } .fa-external-link-square-alt:before, .fa-square-up-right:before { content: "\f360" } .fa-face-kiss-beam:before, .fa-kiss-beam:before { content: "\f597" } .fa-film:before { content: "\f008" } .fa-ruler-horizontal:before { content: "\f547" } .fa-people-robbery:before { content: "\e536" } .fa-lightbulb:before { content: "\f0eb" } .fa-caret-left:before { content: "\f0d9" } .fa-circle-exclamation:before, .fa-exclamation-circle:before { content: "\f06a" } .fa-school-circle-xmark:before { content: "\e56d" } .fa-arrow-right-from-bracket:before, .fa-sign-out:before { content: "\f08b" } .fa-chevron-circle-down:before, .fa-circle-chevron-down:before { content: "\f13a" } .fa-unlock-alt:before, .fa-unlock-keyhole:before { content: "\f13e" } .fa-cloud-showers-heavy:before { content: "\f740" } .fa-headphones-alt:before, .fa-headphones-simple:before { content: "\f58f" } .fa-sitemap:before { content: "\f0e8" } .fa-circle-dollar-to-slot:before, .fa-donate:before { content: "\f4b9" } .fa-memory:before { content: "\f538" } .fa-road-spikes:before { content: "\e568" } .fa-fire-burner:before { content: "\e4f1" } .fa-flag:before { content: "\f024" } .fa-hanukiah:before { content: "\f6e6" } .fa-feather:before { content: "\f52d" } .fa-volume-down:before, .fa-volume-low:before { content: "\f027" } .fa-comment-slash:before { content: "\f4b3" } .fa-cloud-sun-rain:before { content: "\f743" } .fa-compress:before { content: "\f066" } .fa-wheat-alt:before, .fa-wheat-awn:before { content: "\e2cd" } .fa-ankh:before { content: "\f644" } .fa-hands-holding-child:before { content: "\e4fa" } .fa-asterisk:before { content: "\2a" } .fa-check-square:before, .fa-square-check:before { content: "\f14a" } .fa-peseta-sign:before { content: "\e221" } .fa-header:before, .fa-heading:before { content: "\f1dc" } .fa-ghost:before { content: "\f6e2" } .fa-list-squares:before, .fa-list:before { content: "\f03a" } .fa-phone-square-alt:before, .fa-square-phone-flip:before { content: "\f87b" } .fa-cart-plus:before { content: "\f217" } .fa-gamepad:before { content: "\f11b" } .fa-circle-dot:before, .fa-dot-circle:before { content: "\f192" } .fa-dizzy:before, .fa-face-dizzy:before { content: "\f567" } .fa-egg:before { content: "\f7fb" } .fa-house-medical-circle-xmark:before { content: "\e513" } .fa-campground:before { content: "\f6bb" } .fa-folder-plus:before { content: "\f65e" } .fa-futbol-ball:before, .fa-futbol:before, .fa-soccer-ball:before { content: "\f1e3" } .fa-paint-brush:before, .fa-paintbrush:before { content: "\f1fc" } .fa-lock:before { content: "\f023" } .fa-gas-pump:before { content: "\f52f" } .fa-hot-tub-person:before, .fa-hot-tub:before { content: "\f593" } .fa-map-location:before, .fa-map-marked:before { content: "\f59f" } .fa-house-flood-water:before { content: "\e50e" } .fa-tree:before { content: "\f1bb" } .fa-bridge-lock:before { content: "\e4cc" } .fa-sack-dollar:before { content: "\f81d" } .fa-edit:before, .fa-pen-to-square:before { content: "\f044" } .fa-car-side:before { content: "\f5e4" } .fa-share-alt:before, .fa-share-nodes:before { content: "\f1e0" } .fa-heart-circle-minus:before { content: "\e4ff" } .fa-hourglass-2:before, .fa-hourglass-half:before { content: "\f252" } .fa-microscope:before { content: "\f610" } .fa-sink:before { content: "\e06d" } .fa-bag-shopping:before, .fa-shopping-bag:before { content: "\f290" } .fa-arrow-down-z-a:before, .fa-sort-alpha-desc:before, .fa-sort-alpha-down-alt:before { content: "\f881" } .fa-mitten:before { content: "\f7b5" } .fa-person-rays:before { content: "\e54d" } .fa-users:before { content: "\f0c0" } .fa-eye-slash:before { content: "\f070" } .fa-flask-vial:before { content: "\e4f3" } .fa-hand-paper:before, .fa-hand:before { content: "\f256" } .fa-om:before { content: "\f679" } .fa-worm:before { content: "\e599" } .fa-house-circle-xmark:before { content: "\e50b" } .fa-plug:before { content: "\f1e6" } .fa-chevron-up:before { content: "\f077" } .fa-hand-spock:before { content: "\f259" } .fa-stopwatch:before { content: "\f2f2" } .fa-face-kiss:before, .fa-kiss:before { content: "\f596" } .fa-bridge-circle-xmark:before { content: "\e4cb" } .fa-face-grin-tongue:before, .fa-grin-tongue:before { content: "\f589" } .fa-chess-bishop:before { content: "\f43a" } .fa-face-grin-wink:before, .fa-grin-wink:before { content: "\f58c" } .fa-deaf:before, .fa-deafness:before, .fa-ear-deaf:before, .fa-hard-of-hearing:before { content: "\f2a4" } .fa-road-circle-check:before { content: "\e564" } .fa-dice-five:before { content: "\f523" } .fa-rss-square:before, .fa-square-rss:before { content: "\f143" } .fa-land-mine-on:before { content: "\e51b" } .fa-i-cursor:before { content: "\f246" } .fa-stamp:before { content: "\f5bf" } .fa-stairs:before { content: "\e289" } .fa-i:before { content: "\49" } .fa-hryvnia-sign:before, .fa-hryvnia:before { content: "\f6f2" } .fa-pills:before { content: "\f484" } .fa-face-grin-wide:before, .fa-grin-alt:before { content: "\f581" } .fa-tooth:before { content: "\f5c9" } .fa-v:before { content: "\56" } .fa-bicycle:before { content: "\f206" } .fa-rod-asclepius:before, .fa-rod-snake:before, .fa-staff-aesculapius:before, .fa-staff-snake:before { content: "\e579" } .fa-head-side-cough-slash:before { content: "\e062" } .fa-ambulance:before, .fa-truck-medical:before { content: "\f0f9" } .fa-wheat-awn-circle-exclamation:before { content: "\e598" } .fa-snowman:before { content: "\f7d0" } .fa-mortar-pestle:before { content: "\f5a7" } .fa-road-barrier:before { content: "\e562" } .fa-school:before { content: "\f549" } .fa-igloo:before { content: "\f7ae" } .fa-joint:before { content: "\f595" } .fa-angle-right:before { content: "\f105" } .fa-horse:before { content: "\f6f0" } .fa-q:before { content: "\51" } .fa-g:before { content: "\47" } .fa-notes-medical:before { content: "\f481" } .fa-temperature-2:before, .fa-temperature-half:before, .fa-thermometer-2:before, .fa-thermometer-half:before { content: "\f2c9" } .fa-dong-sign:before { content: "\e169" } .fa-capsules:before { content: "\f46b" } .fa-poo-bolt:before, .fa-poo-storm:before { content: "\f75a" } .fa-face-frown-open:before, .fa-frown-open:before { content: "\f57a" } .fa-hand-point-up:before { content: "\f0a6" } .fa-money-bill:before { content: "\f0d6" } .fa-bookmark:before { content: "\f02e" } .fa-align-justify:before { content: "\f039" } .fa-umbrella-beach:before { content: "\f5ca" } .fa-helmet-un:before { content: "\e503" } .fa-bullseye:before { content: "\f140" } .fa-bacon:before { content: "\f7e5" } .fa-hand-point-down:before { content: "\f0a7" } .fa-arrow-up-from-bracket:before { content: "\e09a" } .fa-folder-blank:before, .fa-folder:before { content: "\f07b" } .fa-file-medical-alt:before, .fa-file-waveform:before { content: "\f478" } .fa-radiation:before { content: "\f7b9" } .fa-chart-simple:before { content: "\e473" } .fa-mars-stroke:before { content: "\f229" } .fa-vial:before { content: "\f492" } .fa-dashboard:before, .fa-gauge-med:before, .fa-gauge:before, .fa-tachometer-alt-average:before { content: "\f624" } .fa-magic-wand-sparkles:before, .fa-wand-magic-sparkles:before { content: "\e2ca" } .fa-e:before { content: "\45" } .fa-pen-alt:before, .fa-pen-clip:before { content: "\f305" } .fa-bridge-circle-exclamation:before { content: "\e4ca" } .fa-user:before { content: "\f007" } .fa-school-circle-check:before { content: "\e56b" } .fa-dumpster:before { content: "\f793" } .fa-shuttle-van:before, .fa-van-shuttle:before { content: "\f5b6" } .fa-building-user:before { content: "\e4da" } .fa-caret-square-left:before, .fa-square-caret-left:before { content: "\f191" } .fa-highlighter:before { content: "\f591" } .fa-key:before { content: "\f084" } .fa-bullhorn:before { content: "\f0a1" } .fa-globe:before { content: "\f0ac" } .fa-synagogue:before { content: "\f69b" } .fa-person-half-dress:before { content: "\e548" } .fa-road-bridge:before { content: "\e563" } .fa-location-arrow:before { content: "\f124" } .fa-c:before { content: "\43" } .fa-tablet-button:before { content: "\f10a" } .fa-building-lock:before { content: "\e4d6" } .fa-pizza-slice:before { content: "\f818" } .fa-money-bill-wave:before { content: "\f53a" } .fa-area-chart:before, .fa-chart-area:before { content: "\f1fe" } .fa-house-flag:before { content: "\e50d" } .fa-person-circle-minus:before { content: "\e540" } .fa-ban:before, .fa-cancel:before { content: "\f05e" } .fa-camera-rotate:before { content: "\e0d8" } .fa-air-freshener:before, .fa-spray-can-sparkles:before { content: "\f5d0" } .fa-star:before { content: "\f005" } .fa-repeat:before { content: "\f363" } .fa-cross:before { content: "\f654" } .fa-box:before { content: "\f466" } .fa-venus-mars:before { content: "\f228" } .fa-arrow-pointer:before, .fa-mouse-pointer:before { content: "\f245" } .fa-expand-arrows-alt:before, .fa-maximize:before { content: "\f31e" } .fa-charging-station:before { content: "\f5e7" } .fa-shapes:before, .fa-triangle-circle-square:before { content: "\f61f" } .fa-random:before, .fa-shuffle:before { content: "\f074" } .fa-person-running:before, .fa-running:before { content: "\f70c" } .fa-mobile-retro:before { content: "\e527" } .fa-grip-lines-vertical:before { content: "\f7a5" } .fa-spider:before { content: "\f717" } .fa-hands-bound:before { content: "\e4f9" } .fa-file-invoice-dollar:before { content: "\f571" } .fa-plane-circle-exclamation:before { content: "\e556" } .fa-x-ray:before { content: "\f497" } .fa-spell-check:before { content: "\f891" } .fa-slash:before { content: "\f715" } .fa-computer-mouse:before, .fa-mouse:before { content: "\f8cc" } .fa-arrow-right-to-bracket:before, .fa-sign-in:before { content: "\f090" } .fa-shop-slash:before, .fa-store-alt-slash:before { content: "\e070" } .fa-server:before { content: "\f233" } .fa-virus-covid-slash:before { content: "\e4a9" } .fa-shop-lock:before { content: "\e4a5" } .fa-hourglass-1:before, .fa-hourglass-start:before { content: "\f251" } .fa-blender-phone:before { content: "\f6b6" } .fa-building-wheat:before { content: "\e4db" } .fa-person-breastfeeding:before { content: "\e53a" } .fa-right-to-bracket:before, .fa-sign-in-alt:before { content: "\f2f6" } .fa-venus:before { content: "\f221" } .fa-passport:before { content: "\f5ab" } .fa-heart-pulse:before, .fa-heartbeat:before { content: "\f21e" } .fa-people-carry-box:before, .fa-people-carry:before { content: "\f4ce" } .fa-temperature-high:before { content: "\f769" } .fa-microchip:before { content: "\f2db" } .fa-crown:before { content: "\f521" } .fa-weight-hanging:before { content: "\f5cd" } .fa-xmarks-lines:before { content: "\e59a" } .fa-file-prescription:before { content: "\f572" } .fa-weight-scale:before, .fa-weight:before { content: "\f496" } .fa-user-friends:before, .fa-user-group:before { content: "\f500" } .fa-arrow-up-a-z:before, .fa-sort-alpha-up:before { content: "\f15e" } .fa-chess-knight:before { content: "\f441" } .fa-face-laugh-squint:before, .fa-laugh-squint:before { content: "\f59b" } .fa-wheelchair:before { content: "\f193" } .fa-arrow-circle-up:before, .fa-circle-arrow-up:before { content: "\f0aa" } .fa-toggle-on:before { content: "\f205" } .fa-person-walking:before, .fa-walking:before { content: "\f554" } .fa-l:before { content: "\4c" } .fa-fire:before { content: "\f06d" } .fa-bed-pulse:before, .fa-procedures:before { content: "\f487" } .fa-shuttle-space:before, .fa-space-shuttle:before { content: "\f197" } .fa-face-laugh:before, .fa-laugh:before { content: "\f599" } .fa-folder-open:before { content: "\f07c" } .fa-heart-circle-plus:before { content: "\e500" } .fa-code-fork:before { content: "\e13b" } .fa-city:before { content: "\f64f" } .fa-microphone-alt:before, .fa-microphone-lines:before { content: "\f3c9" } .fa-pepper-hot:before { content: "\f816" } .fa-unlock:before { content: "\f09c" } .fa-colon-sign:before { content: "\e140" } .fa-headset:before { content: "\f590" } .fa-store-slash:before { content: "\e071" } .fa-road-circle-xmark:before { content: "\e566" } .fa-user-minus:before { content: "\f503" } .fa-mars-stroke-up:before, .fa-mars-stroke-v:before { content: "\f22a" } .fa-champagne-glasses:before, .fa-glass-cheers:before { content: "\f79f" } .fa-clipboard:before { content: "\f328" } .fa-house-circle-exclamation:before { content: "\e50a" } .fa-file-arrow-up:before, .fa-file-upload:before { content: "\f574" } .fa-wifi-3:before, .fa-wifi-strong:before, .fa-wifi:before { content: "\f1eb" } .fa-bath:before, .fa-bathtub:before { content: "\f2cd" } .fa-underline:before { content: "\f0cd" } .fa-user-edit:before, .fa-user-pen:before { content: "\f4ff" } .fa-signature:before { content: "\f5b7" } .fa-stroopwafel:before { content: "\f551" } .fa-bold:before { content: "\f032" } .fa-anchor-lock:before { content: "\e4ad" } .fa-building-ngo:before { content: "\e4d7" } .fa-manat-sign:before { content: "\e1d5" } .fa-not-equal:before { content: "\f53e" } .fa-border-style:before, .fa-border-top-left:before { content: "\f853" } .fa-map-location-dot:before, .fa-map-marked-alt:before { content: "\f5a0" } .fa-jedi:before { content: "\f669" } .fa-poll:before, .fa-square-poll-vertical:before { content: "\f681" } .fa-mug-hot:before { content: "\f7b6" } .fa-battery-car:before, .fa-car-battery:before { content: "\f5df" } .fa-gift:before { content: "\f06b" } .fa-dice-two:before { content: "\f528" } .fa-chess-queen:before { content: "\f445" } .fa-glasses:before { content: "\f530" } .fa-chess-board:before { content: "\f43c" } .fa-building-circle-check:before { content: "\e4d2" } .fa-person-chalkboard:before { content: "\e53d" } .fa-mars-stroke-h:before, .fa-mars-stroke-right:before { content: "\f22b" } .fa-hand-back-fist:before, .fa-hand-rock:before { content: "\f255" } .fa-caret-square-up:before, .fa-square-caret-up:before { content: "\f151" } .fa-cloud-showers-water:before { content: "\e4e4" } .fa-bar-chart:before, .fa-chart-bar:before { content: "\f080" } .fa-hands-bubbles:before, .fa-hands-wash:before { content: "\e05e" } .fa-less-than-equal:before { content: "\f537" } .fa-train:before { content: "\f238" } .fa-eye-low-vision:before, .fa-low-vision:before { content: "\f2a8" } .fa-crow:before { content: "\f520" } .fa-sailboat:before { content: "\e445" } .fa-window-restore:before { content: "\f2d2" } .fa-plus-square:before, .fa-square-plus:before { content: "\f0fe" } .fa-torii-gate:before { content: "\f6a1" } .fa-frog:before { content: "\f52e" } .fa-bucket:before { content: "\e4cf" } .fa-image:before { content: "\f03e" } .fa-microphone:before { content: "\f130" } .fa-cow:before { content: "\f6c8" } .fa-caret-up:before { content: "\f0d8" } .fa-screwdriver:before { content: "\f54a" } .fa-folder-closed:before { content: "\e185" } .fa-house-tsunami:before { content: "\e515" } .fa-square-nfi:before { content: "\e576" } .fa-arrow-up-from-ground-water:before { content: "\e4b5" } .fa-glass-martini-alt:before, .fa-martini-glass:before { content: "\f57b" } .fa-rotate-back:before, .fa-rotate-backward:before, .fa-rotate-left:before, .fa-undo-alt:before { content: "\f2ea" } .fa-columns:before, .fa-table-columns:before { content: "\f0db" } .fa-lemon:before { content: "\f094" } .fa-head-side-mask:before { content: "\e063" } .fa-handshake:before { content: "\f2b5" } .fa-gem:before { content: "\f3a5" } .fa-dolly-box:before, .fa-dolly:before { content: "\f472" } .fa-smoking:before { content: "\f48d" } .fa-compress-arrows-alt:before, .fa-minimize:before { content: "\f78c" } .fa-monument:before { content: "\f5a6" } .fa-snowplow:before { content: "\f7d2" } .fa-angle-double-right:before, .fa-angles-right:before { content: "\f101" } .fa-cannabis:before { content: "\f55f" } .fa-circle-play:before, .fa-play-circle:before { content: "\f144" } .fa-tablets:before { content: "\f490" } .fa-ethernet:before { content: "\f796" } .fa-eur:before, .fa-euro-sign:before, .fa-euro:before { content: "\f153" } .fa-chair:before { content: "\f6c0" } .fa-check-circle:before, .fa-circle-check:before { content: "\f058" } .fa-circle-stop:before, .fa-stop-circle:before { content: "\f28d" } .fa-compass-drafting:before, .fa-drafting-compass:before { content: "\f568" } .fa-plate-wheat:before { content: "\e55a" } .fa-icicles:before { content: "\f7ad" } .fa-person-shelter:before { content: "\e54f" } .fa-neuter:before { content: "\f22c" } .fa-id-badge:before { content: "\f2c1" } .fa-marker:before { content: "\f5a1" } .fa-face-laugh-beam:before, .fa-laugh-beam:before { content: "\f59a" } .fa-helicopter-symbol:before { content: "\e502" } .fa-universal-access:before { content: "\f29a" } .fa-chevron-circle-up:before, .fa-circle-chevron-up:before { content: "\f139" } .fa-lari-sign:before { content: "\e1c8" } .fa-volcano:before { content: "\f770" } .fa-person-walking-dashed-line-arrow-right:before { content: "\e553" } .fa-gbp:before, .fa-pound-sign:before, .fa-sterling-sign:before { content: "\f154" } .fa-viruses:before { content: "\e076" } .fa-square-person-confined:before { content: "\e577" } .fa-user-tie:before { content: "\f508" } .fa-arrow-down-long:before, .fa-long-arrow-down:before { content: "\f175" } .fa-tent-arrow-down-to-line:before { content: "\e57e" } .fa-certificate:before { content: "\f0a3" } .fa-mail-reply-all:before, .fa-reply-all:before { content: "\f122" } .fa-suitcase:before { content: "\f0f2" } .fa-person-skating:before, .fa-skating:before { content: "\f7c5" } .fa-filter-circle-dollar:before, .fa-funnel-dollar:before { content: "\f662" } .fa-camera-retro:before { content: "\f083" } .fa-arrow-circle-down:before, .fa-circle-arrow-down:before { content: "\f0ab" } .fa-arrow-right-to-file:before, .fa-file-import:before { content: "\f56f" } .fa-external-link-square:before, .fa-square-arrow-up-right:before { content: "\f14c" } .fa-box-open:before { content: "\f49e" } .fa-scroll:before { content: "\f70e" } .fa-spa:before { content: "\f5bb" } .fa-location-pin-lock:before { content: "\e51f" } .fa-pause:before { content: "\f04c" } .fa-hill-avalanche:before { content: "\e507" } .fa-temperature-0:before, .fa-temperature-empty:before, .fa-thermometer-0:before, .fa-thermometer-empty:before { content: "\f2cb" } .fa-bomb:before { content: "\f1e2" } .fa-registered:before { content: "\f25d" } .fa-address-card:before, .fa-contact-card:before, .fa-vcard:before { content: "\f2bb" } .fa-balance-scale-right:before, .fa-scale-unbalanced-flip:before { content: "\f516" } .fa-subscript:before { content: "\f12c" } .fa-diamond-turn-right:before, .fa-directions:before { content: "\f5eb" } .fa-burst:before { content: "\e4dc" } .fa-house-laptop:before, .fa-laptop-house:before { content: "\e066" } .fa-face-tired:before, .fa-tired:before { content: "\f5c8" } .fa-money-bills:before { content: "\e1f3" } .fa-smog:before { content: "\f75f" } .fa-crutch:before { content: "\f7f7" } .fa-cloud-arrow-up:before, .fa-cloud-upload-alt:before, .fa-cloud-upload:before { content: "\f0ee" } .fa-palette:before { content: "\f53f" } .fa-arrows-turn-right:before { content: "\e4c0" } .fa-vest:before { content: "\e085" } .fa-ferry:before { content: "\e4ea" } .fa-arrows-down-to-people:before { content: "\e4b9" } .fa-seedling:before, .fa-sprout:before { content: "\f4d8" } .fa-arrows-alt-h:before, .fa-left-right:before { content: "\f337" } .fa-boxes-packing:before { content: "\e4c7" } .fa-arrow-circle-left:before, .fa-circle-arrow-left:before { content: "\f0a8" } .fa-group-arrows-rotate:before { content: "\e4f6" } .fa-bowl-food:before { content: "\e4c6" } .fa-candy-cane:before { content: "\f786" } .fa-arrow-down-wide-short:before, .fa-sort-amount-asc:before, .fa-sort-amount-down:before { content: "\f160" } .fa-cloud-bolt:before, .fa-thunderstorm:before { content: "\f76c" } .fa-remove-format:before, .fa-text-slash:before { content: "\f87d" } .fa-face-smile-wink:before, .fa-smile-wink:before { content: "\f4da" } .fa-file-word:before { content: "\f1c2" } .fa-file-powerpoint:before { content: "\f1c4" } .fa-arrows-h:before, .fa-arrows-left-right:before { content: "\f07e" } .fa-house-lock:before { content: "\e510" } .fa-cloud-arrow-down:before, .fa-cloud-download-alt:before, .fa-cloud-download:before { content: "\f0ed" } .fa-children:before { content: "\e4e1" } .fa-blackboard:before, .fa-chalkboard:before { content: "\f51b" } .fa-user-alt-slash:before, .fa-user-large-slash:before { content: "\f4fa" } .fa-envelope-open:before { content: "\f2b6" } .fa-handshake-alt-slash:before, .fa-handshake-simple-slash:before { content: "\e05f" } .fa-mattress-pillow:before { content: "\e525" } .fa-guarani-sign:before { content: "\e19a" } .fa-arrows-rotate:before, .fa-refresh:before, .fa-sync:before { content: "\f021" } .fa-fire-extinguisher:before { content: "\f134" } .fa-cruzeiro-sign:before { content: "\e152" } .fa-greater-than-equal:before { content: "\f532" } .fa-shield-alt:before, .fa-shield-halved:before { content: "\f3ed" } .fa-atlas:before, .fa-book-atlas:before { content: "\f558" } .fa-virus:before { content: "\e074" } .fa-envelope-circle-check:before { content: "\e4e8" } .fa-layer-group:before { content: "\f5fd" } .fa-arrows-to-dot:before { content: "\e4be" } .fa-archway:before { content: "\f557" } .fa-heart-circle-check:before { content: "\e4fd" } .fa-house-chimney-crack:before, .fa-house-damage:before { content: "\f6f1" } .fa-file-archive:before, .fa-file-zipper:before { content: "\f1c6" } .fa-square:before { content: "\f0c8" } .fa-glass-martini:before, .fa-martini-glass-empty:before { content: "\f000" } .fa-couch:before { content: "\f4b8" } .fa-cedi-sign:before { content: "\e0df" } .fa-italic:before { content: "\f033" } .fa-church:before { content: "\f51d" } .fa-comments-dollar:before { content: "\f653" } .fa-democrat:before { content: "\f747" } .fa-z:before { content: "\5a" } .fa-person-skiing:before, .fa-skiing:before { content: "\f7c9" } .fa-road-lock:before { content: "\e567" } .fa-a:before { content: "\41" } .fa-temperature-arrow-down:before, .fa-temperature-down:before { content: "\e03f" } .fa-feather-alt:before, .fa-feather-pointed:before { content: "\f56b" } .fa-p:before { content: "\50" } .fa-snowflake:before { content: "\f2dc" } .fa-newspaper:before { content: "\f1ea" } .fa-ad:before, .fa-rectangle-ad:before { content: "\f641" } .fa-arrow-circle-right:before, .fa-circle-arrow-right:before { content: "\f0a9" } .fa-filter-circle-xmark:before { content: "\e17b" } .fa-locust:before { content: "\e520" } .fa-sort:before, .fa-unsorted:before { content: "\f0dc" } .fa-list-1-2:before, .fa-list-numeric:before, .fa-list-ol:before { content: "\f0cb" } .fa-person-dress-burst:before { content: "\e544" } .fa-money-check-alt:before, .fa-money-check-dollar:before { content: "\f53d" } .fa-vector-square:before { content: "\f5cb" } .fa-bread-slice:before { content: "\f7ec" } .fa-language:before { content: "\f1ab" } .fa-face-kiss-wink-heart:before, .fa-kiss-wink-heart:before { content: "\f598" } .fa-filter:before { content: "\f0b0" } .fa-question:before { content: "\3f" } .fa-file-signature:before { content: "\f573" } .fa-arrows-alt:before, .fa-up-down-left-right:before { content: "\f0b2" } .fa-house-chimney-user:before { content: "\e065" } .fa-hand-holding-heart:before { content: "\f4be" } .fa-puzzle-piece:before { content: "\f12e" } .fa-money-check:before { content: "\f53c" } .fa-star-half-alt:before, .fa-star-half-stroke:before { content: "\f5c0" } .fa-code:before { content: "\f121" } .fa-glass-whiskey:before, .fa-whiskey-glass:before { content: "\f7a0" } .fa-building-circle-exclamation:before { content: "\e4d3" } .fa-magnifying-glass-chart:before { content: "\e522" } .fa-arrow-up-right-from-square:before, .fa-external-link:before { content: "\f08e" } .fa-cubes-stacked:before { content: "\e4e6" } .fa-krw:before, .fa-won-sign:before, .fa-won:before { content: "\f159" } .fa-virus-covid:before { content: "\e4a8" } .fa-austral-sign:before { content: "\e0a9" } .fa-f:before { content: "\46" } .fa-leaf:before { content: "\f06c" } .fa-road:before { content: "\f018" } .fa-cab:before, .fa-taxi:before { content: "\f1ba" } .fa-person-circle-plus:before { content: "\e541" } .fa-chart-pie:before, .fa-pie-chart:before { content: "\f200" } .fa-bolt-lightning:before { content: "\e0b7" } .fa-sack-xmark:before { content: "\e56a" } .fa-file-excel:before { content: "\f1c3" } .fa-file-contract:before { content: "\f56c" } .fa-fish-fins:before { content: "\e4f2" } .fa-building-flag:before { content: "\e4d5" } .fa-face-grin-beam:before, .fa-grin-beam:before { content: "\f582" } .fa-object-ungroup:before { content: "\f248" } .fa-poop:before { content: "\f619" } .fa-location-pin:before, .fa-map-marker:before { content: "\f041" } .fa-kaaba:before { content: "\f66b" } .fa-toilet-paper:before { content: "\f71e" } .fa-hard-hat:before, .fa-hat-hard:before, .fa-helmet-safety:before { content: "\f807" } .fa-eject:before { content: "\f052" } .fa-arrow-alt-circle-right:before, .fa-circle-right:before { content: "\f35a" } .fa-plane-circle-check:before { content: "\e555" } .fa-face-rolling-eyes:before, .fa-meh-rolling-eyes:before { content: "\f5a5" } .fa-object-group:before { content: "\f247" } .fa-chart-line:before, .fa-line-chart:before { content: "\f201" } .fa-mask-ventilator:before { content: "\e524" } .fa-arrow-right:before { content: "\f061" } .fa-map-signs:before, .fa-signs-post:before { content: "\f277" } .fa-cash-register:before { content: "\f788" } .fa-person-circle-question:before { content: "\e542" } .fa-h:before { content: "\48" } .fa-tarp:before { content: "\e57b" } .fa-screwdriver-wrench:before, .fa-tools:before { content: "\f7d9" } .fa-arrows-to-eye:before { content: "\e4bf" } .fa-plug-circle-bolt:before { content: "\e55b" } .fa-heart:before { content: "\f004" } .fa-mars-and-venus:before { content: "\f224" } .fa-home-user:before, .fa-house-user:before { content: "\e1b0" } .fa-dumpster-fire:before { content: "\f794" } .fa-house-crack:before { content: "\e3b1" } .fa-cocktail:before, .fa-martini-glass-citrus:before { content: "\f561" } .fa-face-surprise:before, .fa-surprise:before { content: "\f5c2" } .fa-bottle-water:before { content: "\e4c5" } .fa-circle-pause:before, .fa-pause-circle:before { content: "\f28b" } .fa-toilet-paper-slash:before { content: "\e072" } .fa-apple-alt:before, .fa-apple-whole:before { content: "\f5d1" } .fa-kitchen-set:before { content: "\e51a" } .fa-r:before { content: "\52" } .fa-temperature-1:before, .fa-temperature-quarter:before, .fa-thermometer-1:before, .fa-thermometer-quarter:before { content: "\f2ca" } .fa-cube:before { content: "\f1b2" } .fa-bitcoin-sign:before { content: "\e0b4" } .fa-shield-dog:before { content: "\e573" } .fa-solar-panel:before { content: "\f5ba" } .fa-lock-open:before { content: "\f3c1" } .fa-elevator:before { content: "\e16d" } .fa-money-bill-transfer:before { content: "\e528" } .fa-money-bill-trend-up:before { content: "\e529" } .fa-house-flood-water-circle-arrow-right:before { content: "\e50f" } .fa-poll-h:before, .fa-square-poll-horizontal:before { content: "\f682" } .fa-circle:before { content: "\f111" } .fa-backward-fast:before, .fa-fast-backward:before { content: "\f049" } .fa-recycle:before { content: "\f1b8" } .fa-user-astronaut:before { content: "\f4fb" } .fa-plane-slash:before { content: "\e069" } .fa-trademark:before { content: "\f25c" } .fa-basketball-ball:before, .fa-basketball:before { content: "\f434" } .fa-satellite-dish:before { content: "\f7c0" } .fa-arrow-alt-circle-up:before, .fa-circle-up:before { content: "\f35b" } .fa-mobile-alt:before, .fa-mobile-screen-button:before { content: "\f3cd" } .fa-volume-high:before, .fa-volume-up:before { content: "\f028" } .fa-users-rays:before { content: "\e593" } .fa-wallet:before { content: "\f555" } .fa-clipboard-check:before { content: "\f46c" } .fa-file-audio:before { content: "\f1c7" } .fa-burger:before, .fa-hamburger:before { content: "\f805" } .fa-wrench:before { content: "\f0ad" } .fa-bugs:before { content: "\e4d0" } .fa-rupee-sign:before, .fa-rupee:before { content: "\f156" } .fa-file-image:before { content: "\f1c5" } .fa-circle-question:before, .fa-question-circle:before { content: "\f059" } .fa-plane-departure:before { content: "\f5b0" } .fa-handshake-slash:before { content: "\e060" } .fa-book-bookmark:before { content: "\e0bb" } .fa-code-branch:before { content: "\f126" } .fa-hat-cowboy:before { content: "\f8c0" } .fa-bridge:before { content: "\e4c8" } .fa-phone-alt:before, .fa-phone-flip:before { content: "\f879" } .fa-truck-front:before { content: "\e2b7" } .fa-cat:before { content: "\f6be" } .fa-anchor-circle-exclamation:before { content: "\e4ab" } .fa-truck-field:before { content: "\e58d" } .fa-route:before { content: "\f4d7" } .fa-clipboard-question:before { content: "\e4e3" } .fa-panorama:before { content: "\e209" } .fa-comment-medical:before { content: "\f7f5" } .fa-teeth-open:before { content: "\f62f" } .fa-file-circle-minus:before { content: "\e4ed" } .fa-tags:before { content: "\f02c" } .fa-wine-glass:before { content: "\f4e3" } .fa-fast-forward:before, .fa-forward-fast:before { content: "\f050" } .fa-face-meh-blank:before, .fa-meh-blank:before { content: "\f5a4" } .fa-parking:before, .fa-square-parking:before { content: "\f540" } .fa-house-signal:before { content: "\e012" } .fa-bars-progress:before, .fa-tasks-alt:before { content: "\f828" } .fa-faucet-drip:before { content: "\e006" } .fa-cart-flatbed:before, .fa-dolly-flatbed:before { content: "\f474" } .fa-ban-smoking:before, .fa-smoking-ban:before { content: "\f54d" } .fa-terminal:before { content: "\f120" } .fa-mobile-button:before { content: "\f10b" } .fa-house-medical-flag:before { content: "\e514" } .fa-basket-shopping:before, .fa-shopping-basket:before { content: "\f291" } .fa-tape:before { content: "\f4db" } .fa-bus-alt:before, .fa-bus-simple:before { content: "\f55e" } .fa-eye:before { content: "\f06e" } .fa-face-sad-cry:before, .fa-sad-cry:before { content: "\f5b3" } .fa-audio-description:before { content: "\f29e" } .fa-person-military-to-person:before { content: "\e54c" } .fa-file-shield:before { content: "\e4f0" } .fa-user-slash:before { content: "\f506" } .fa-pen:before { content: "\f304" } .fa-tower-observation:before { content: "\e586" } .fa-file-code:before { content: "\f1c9" } .fa-signal-5:before, .fa-signal-perfect:before, .fa-signal:before { content: "\f012" } .fa-bus:before { content: "\f207" } .fa-heart-circle-xmark:before { content: "\e501" } .fa-home-lg:before, .fa-house-chimney:before { content: "\e3af" } .fa-window-maximize:before { content: "\f2d0" } .fa-face-frown:before, .fa-frown:before { content: "\f119" } .fa-prescription:before { content: "\f5b1" } .fa-shop:before, .fa-store-alt:before { content: "\f54f" } .fa-floppy-disk:before, .fa-save:before { content: "\f0c7" } .fa-vihara:before { content: "\f6a7" } .fa-balance-scale-left:before, .fa-scale-unbalanced:before { content: "\f515" } .fa-sort-asc:before, .fa-sort-up:before { content: "\f0de" } .fa-comment-dots:before, .fa-commenting:before { content: "\f4ad" } .fa-plant-wilt:before { content: "\e5aa" } .fa-diamond:before { content: "\f219" } .fa-face-grin-squint:before, .fa-grin-squint:before { content: "\f585" } .fa-hand-holding-dollar:before, .fa-hand-holding-usd:before { content: "\f4c0" } .fa-bacterium:before { content: "\e05a" } .fa-hand-pointer:before { content: "\f25a" } .fa-drum-steelpan:before { content: "\f56a" } .fa-hand-scissors:before { content: "\f257" } .fa-hands-praying:before, .fa-praying-hands:before { content: "\f684" } .fa-arrow-right-rotate:before, .fa-arrow-rotate-forward:before, .fa-arrow-rotate-right:before, .fa-redo:before { content: "\f01e" } .fa-biohazard:before { content: "\f780" } .fa-location-crosshairs:before, .fa-location:before { content: "\f601" } .fa-mars-double:before { content: "\f227" } .fa-child-dress:before { content: "\e59c" } .fa-users-between-lines:before { content: "\e591" } .fa-lungs-virus:before { content: "\e067" } .fa-face-grin-tears:before, .fa-grin-tears:before { content: "\f588" } .fa-phone:before { content: "\f095" } .fa-calendar-times:before, .fa-calendar-xmark:before { content: "\f273" } .fa-child-reaching:before { content: "\e59d" } .fa-head-side-virus:before { content: "\e064" } .fa-user-cog:before, .fa-user-gear:before { content: "\f4fe" } .fa-arrow-up-1-9:before, .fa-sort-numeric-up:before { content: "\f163" } .fa-door-closed:before { content: "\f52a" } .fa-shield-virus:before { content: "\e06c" } .fa-dice-six:before { content: "\f526" } .fa-mosquito-net:before { content: "\e52c" } .fa-bridge-water:before { content: "\e4ce" } .fa-person-booth:before { content: "\f756" } .fa-text-width:before { content: "\f035" } .fa-hat-wizard:before { content: "\f6e8" } .fa-pen-fancy:before { content: "\f5ac" } .fa-digging:before, .fa-person-digging:before { content: "\f85e" } .fa-trash:before { content: "\f1f8" } .fa-gauge-simple-med:before, .fa-gauge-simple:before, .fa-tachometer-average:before { content: "\f629" } .fa-book-medical:before { content: "\f7e6" } .fa-poo:before { content: "\f2fe" } .fa-quote-right-alt:before, .fa-quote-right:before { content: "\f10e" } .fa-shirt:before, .fa-t-shirt:before, .fa-tshirt:before { content: "\f553" } .fa-cubes:before { content: "\f1b3" } .fa-divide:before { content: "\f529" } .fa-tenge-sign:before, .fa-tenge:before { content: "\f7d7" } .fa-headphones:before { content: "\f025" } .fa-hands-holding:before { content: "\f4c2" } .fa-hands-clapping:before { content: "\e1a8" } .fa-republican:before { content: "\f75e" } .fa-arrow-left:before { content: "\f060" } .fa-person-circle-xmark:before { content: "\e543" } .fa-ruler:before { content: "\f545" } .fa-align-left:before { content: "\f036" } .fa-dice-d6:before { content: "\f6d1" } .fa-restroom:before { content: "\f7bd" } .fa-j:before { content: "\4a" } .fa-users-viewfinder:before { content: "\e595" } .fa-file-video:before { content: "\f1c8" } .fa-external-link-alt:before, .fa-up-right-from-square:before { content: "\f35d" } .fa-table-cells:before, .fa-th:before { content: "\f00a" } .fa-file-pdf:before { content: "\f1c1" } .fa-bible:before, .fa-book-bible:before { content: "\f647" } .fa-o:before { content: "\4f" } .fa-medkit:before, .fa-suitcase-medical:before { content: "\f0fa" } .fa-user-secret:before { content: "\f21b" } .fa-otter:before { content: "\f700" } .fa-female:before, .fa-person-dress:before { content: "\f182" } .fa-comment-dollar:before { content: "\f651" } .fa-briefcase-clock:before, .fa-business-time:before { content: "\f64a" } .fa-table-cells-large:before, .fa-th-large:before { content: "\f009" } .fa-book-tanakh:before, .fa-tanakh:before { content: "\f827" } .fa-phone-volume:before, .fa-volume-control-phone:before { content: "\f2a0" } .fa-hat-cowboy-side:before { content: "\f8c1" } .fa-clipboard-user:before { content: "\f7f3" } .fa-child:before { content: "\f1ae" } .fa-lira-sign:before { content: "\f195" } .fa-satellite:before { content: "\f7bf" } .fa-plane-lock:before { content: "\e558" } .fa-tag:before { content: "\f02b" } .fa-comment:before { content: "\f075" } .fa-birthday-cake:before, .fa-cake-candles:before, .fa-cake:before { content: "\f1fd" } .fa-envelope:before { content: "\f0e0" } .fa-angle-double-up:before, .fa-angles-up:before { content: "\f102" } .fa-paperclip:before { content: "\f0c6" } .fa-arrow-right-to-city:before { content: "\e4b3" } .fa-ribbon:before { content: "\f4d6" } .fa-lungs:before { content: "\f604" } .fa-arrow-up-9-1:before, .fa-sort-numeric-up-alt:before { content: "\f887" } .fa-litecoin-sign:before { content: "\e1d3" } .fa-border-none:before { content: "\f850" } .fa-circle-nodes:before { content: "\e4e2" } .fa-parachute-box:before { content: "\f4cd" } .fa-indent:before { content: "\f03c" } .fa-truck-field-un:before { content: "\e58e" } .fa-hourglass-empty:before, .fa-hourglass:before { content: "\f254" } .fa-mountain:before { content: "\f6fc" } .fa-user-doctor:before, .fa-user-md:before { content: "\f0f0" } .fa-circle-info:before, .fa-info-circle:before { content: "\f05a" } .fa-cloud-meatball:before { content: "\f73b" } .fa-camera-alt:before, .fa-camera:before { content: "\f030" } .fa-square-virus:before { content: "\e578" } .fa-meteor:before { content: "\f753" } .fa-car-on:before { content: "\e4dd" } .fa-sleigh:before { content: "\f7cc" } .fa-arrow-down-1-9:before, .fa-sort-numeric-asc:before, .fa-sort-numeric-down:before { content: "\f162" } .fa-hand-holding-droplet:before, .fa-hand-holding-water:before { content: "\f4c1" } .fa-water:before { content: "\f773" } .fa-calendar-check:before { content: "\f274" } .fa-braille:before { content: "\f2a1" } .fa-prescription-bottle-alt:before, .fa-prescription-bottle-medical:before { content: "\f486" } .fa-landmark:before { content: "\f66f" } .fa-truck:before { content: "\f0d1" } .fa-crosshairs:before { content: "\f05b" } .fa-person-cane:before { content: "\e53c" } .fa-tent:before { content: "\e57d" } .fa-vest-patches:before { content: "\e086" } .fa-check-double:before { content: "\f560" } .fa-arrow-down-a-z:before, .fa-sort-alpha-asc:before, .fa-sort-alpha-down:before { content: "\f15d" } .fa-money-bill-wheat:before { content: "\e52a" } .fa-cookie:before { content: "\f563" } .fa-arrow-left-rotate:before, .fa-arrow-rotate-back:before, .fa-arrow-rotate-backward:before, .fa-arrow-rotate-left:before, .fa-undo:before { content: "\f0e2" } .fa-hard-drive:before, .fa-hdd:before { content: "\f0a0" } .fa-face-grin-squint-tears:before, .fa-grin-squint-tears:before { content: "\f586" } .fa-dumbbell:before { content: "\f44b" } .fa-list-alt:before, .fa-rectangle-list:before { content: "\f022" } .fa-tarp-droplet:before { content: "\e57c" } .fa-house-medical-circle-check:before { content: "\e511" } .fa-person-skiing-nordic:before, .fa-skiing-nordic:before { content: "\f7ca" } .fa-calendar-plus:before { content: "\f271" } .fa-plane-arrival:before { content: "\f5af" } .fa-arrow-alt-circle-left:before, .fa-circle-left:before { content: "\f359" } .fa-subway:before, .fa-train-subway:before { content: "\f239" } .fa-chart-gantt:before { content: "\e0e4" } .fa-indian-rupee-sign:before, .fa-indian-rupee:before, .fa-inr:before { content: "\e1bc" } .fa-crop-alt:before, .fa-crop-simple:before { content: "\f565" } .fa-money-bill-1:before, .fa-money-bill-alt:before { content: "\f3d1" } .fa-left-long:before, .fa-long-arrow-alt-left:before { content: "\f30a" } .fa-dna:before { content: "\f471" } .fa-virus-slash:before { content: "\e075" } .fa-minus:before, .fa-subtract:before { content: "\f068" } .fa-child-rifle:before { content: "\e4e0" } .fa-chess:before { content: "\f439" } .fa-arrow-left-long:before, .fa-long-arrow-left:before { content: "\f177" } .fa-plug-circle-check:before { content: "\e55c" } .fa-street-view:before { content: "\f21d" } .fa-franc-sign:before { content: "\e18f" } .fa-volume-off:before { content: "\f026" } .fa-american-sign-language-interpreting:before, .fa-asl-interpreting:before, .fa-hands-american-sign-language-interpreting:before, .fa-hands-asl-interpreting:before { content: "\f2a3" } .fa-cog:before, .fa-gear:before { content: "\f013" } .fa-droplet-slash:before, .fa-tint-slash:before { content: "\f5c7" } .fa-mosque:before { content: "\f678" } .fa-mosquito:before { content: "\e52b" } .fa-star-of-david:before { content: "\f69a" } .fa-person-military-rifle:before { content: "\e54b" } .fa-cart-shopping:before, .fa-shopping-cart:before { content: "\f07a" } .fa-vials:before { content: "\f493" } .fa-plug-circle-plus:before { content: "\e55f" } .fa-place-of-worship:before { content: "\f67f" } .fa-grip-vertical:before { content: "\f58e" } .fa-arrow-turn-up:before, .fa-level-up:before { content: "\f148" } .fa-u:before { content: "\55" } .fa-square-root-alt:before, .fa-square-root-variable:before { content: "\f698" } .fa-clock-four:before, .fa-clock:before { content: "\f017" } .fa-backward-step:before, .fa-step-backward:before { content: "\f048" } .fa-pallet:before { content: "\f482" } .fa-faucet:before { content: "\e005" } .fa-baseball-bat-ball:before { content: "\f432" } .fa-s:before { content: "\53" } .fa-timeline:before { content: "\e29c" } .fa-keyboard:before { content: "\f11c" } .fa-caret-down:before { content: "\f0d7" } .fa-clinic-medical:before, .fa-house-chimney-medical:before { content: "\f7f2" } .fa-temperature-3:before, .fa-temperature-three-quarters:before, .fa-thermometer-3:before, .fa-thermometer-three-quarters:before { content: "\f2c8" } .fa-mobile-android-alt:before, .fa-mobile-screen:before { content: "\f3cf" } .fa-plane-up:before { content: "\e22d" } .fa-piggy-bank:before { content: "\f4d3" } .fa-battery-3:before, .fa-battery-half:before { content: "\f242" } .fa-mountain-city:before { content: "\e52e" } .fa-coins:before { content: "\f51e" } .fa-khanda:before { content: "\f66d" } .fa-sliders-h:before, .fa-sliders:before { content: "\f1de" } .fa-folder-tree:before { content: "\f802" } .fa-network-wired:before { content: "\f6ff" } .fa-map-pin:before { content: "\f276" } .fa-hamsa:before { content: "\f665" } .fa-cent-sign:before { content: "\e3f5" } .fa-flask:before { content: "\f0c3" } .fa-person-pregnant:before { content: "\e31e" } .fa-wand-sparkles:before { content: "\f72b" } .fa-ellipsis-v:before, .fa-ellipsis-vertical:before { content: "\f142" } .fa-ticket:before { content: "\f145" } .fa-power-off:before { content: "\f011" } .fa-long-arrow-alt-right:before, .fa-right-long:before { content: "\f30b" } .fa-flag-usa:before { content: "\f74d" } .fa-laptop-file:before { content: "\e51d" } .fa-teletype:before, .fa-tty:before { content: "\f1e4" } .fa-diagram-next:before { content: "\e476" } .fa-person-rifle:before { content: "\e54e" } .fa-house-medical-circle-exclamation:before { content: "\e512" } .fa-closed-captioning:before { content: "\f20a" } .fa-hiking:before, .fa-person-hiking:before { content: "\f6ec" } .fa-venus-double:before { content: "\f226" } .fa-images:before { content: "\f302" } .fa-calculator:before { content: "\f1ec" } .fa-people-pulling:before { content: "\e535" } .fa-n:before { content: "\4e" } .fa-cable-car:before, .fa-tram:before { content: "\f7da" } .fa-cloud-rain:before { content: "\f73d" } .fa-building-circle-xmark:before { content: "\e4d4" } .fa-ship:before { content: "\f21a" } .fa-arrows-down-to-line:before { content: "\e4b8" } .fa-download:before { content: "\f019" } .fa-face-grin:before, .fa-grin:before { content: "\f580" } .fa-backspace:before, .fa-delete-left:before { content: "\f55a" } .fa-eye-dropper-empty:before, .fa-eye-dropper:before, .fa-eyedropper:before { content: "\f1fb" } .fa-file-circle-check:before { content: "\e5a0" } .fa-forward:before { content: "\f04e" } .fa-mobile-android:before, .fa-mobile-phone:before, .fa-mobile:before { content: "\f3ce" } .fa-face-meh:before, .fa-meh:before { content: "\f11a" } .fa-align-center:before { content: "\f037" } .fa-book-dead:before, .fa-book-skull:before { content: "\f6b7" } .fa-drivers-license:before, .fa-id-card:before { content: "\f2c2" } .fa-dedent:before, .fa-outdent:before { content: "\f03b" } .fa-heart-circle-exclamation:before { content: "\e4fe" } .fa-home-alt:before, .fa-home-lg-alt:before, .fa-home:before, .fa-house:before { content: "\f015" } .fa-calendar-week:before { content: "\f784" } .fa-laptop-medical:before { content: "\f812" } .fa-b:before { content: "\42" } .fa-file-medical:before { content: "\f477" } .fa-dice-one:before { content: "\f525" } .fa-kiwi-bird:before { content: "\f535" } .fa-arrow-right-arrow-left:before, .fa-exchange:before { content: "\f0ec" } .fa-redo-alt:before, .fa-rotate-forward:before, .fa-rotate-right:before { content: "\f2f9" } .fa-cutlery:before, .fa-utensils:before { content: "\f2e7" } .fa-arrow-up-wide-short:before, .fa-sort-amount-up:before { content: "\f161" } .fa-mill-sign:before { content: "\e1ed" } .fa-bowl-rice:before { content: "\e2eb" } .fa-skull:before { content: "\f54c" } .fa-broadcast-tower:before, .fa-tower-broadcast:before { content: "\f519" } .fa-truck-pickup:before { content: "\f63c" } .fa-long-arrow-alt-up:before, .fa-up-long:before { content: "\f30c" } .fa-stop:before { content: "\f04d" } .fa-code-merge:before { content: "\f387" } .fa-upload:before { content: "\f093" } .fa-hurricane:before { content: "\f751" } .fa-mound:before { content: "\e52d" } .fa-toilet-portable:before { content: "\e583" } .fa-compact-disc:before { content: "\f51f" } .fa-file-arrow-down:before, .fa-file-download:before { content: "\f56d" } .fa-caravan:before { content: "\f8ff" } .fa-shield-cat:before { content: "\e572" } .fa-bolt:before, .fa-zap:before { content: "\f0e7" } .fa-glass-water:before { content: "\e4f4" } .fa-oil-well:before { content: "\e532" } .fa-vault:before { content: "\e2c5" } .fa-mars:before { content: "\f222" } .fa-toilet:before { content: "\f7d8" } .fa-plane-circle-xmark:before { content: "\e557" } .fa-cny:before, .fa-jpy:before, .fa-rmb:before, .fa-yen-sign:before, .fa-yen:before { content: "\f157" } .fa-rouble:before, .fa-rub:before, .fa-ruble-sign:before, .fa-ruble:before { content: "\f158" } .fa-sun:before { content: "\f185" } .fa-guitar:before { content: "\f7a6" } .fa-face-laugh-wink:before, .fa-laugh-wink:before { content: "\f59c" } .fa-horse-head:before { content: "\f7ab" } .fa-bore-hole:before { content: "\e4c3" } .fa-industry:before { content: "\f275" } .fa-arrow-alt-circle-down:before, .fa-circle-down:before { content: "\f358" } .fa-arrows-turn-to-dots:before { content: "\e4c1" } .fa-florin-sign:before { content: "\e184" } .fa-arrow-down-short-wide:before, .fa-sort-amount-desc:before, .fa-sort-amount-down-alt:before { content: "\f884" } .fa-less-than:before { content: "\3c" } .fa-angle-down:before { content: "\f107" } .fa-car-tunnel:before { content: "\e4de" } .fa-head-side-cough:before { content: "\e061" } .fa-grip-lines:before { content: "\f7a4" } .fa-thumbs-down:before { content: "\f165" } .fa-user-lock:before { content: "\f502" } .fa-arrow-right-long:before, .fa-long-arrow-right:before { content: "\f178" } .fa-anchor-circle-xmark:before { content: "\e4ac" } .fa-ellipsis-h:before, .fa-ellipsis:before { content: "\f141" } .fa-chess-pawn:before { content: "\f443" } .fa-first-aid:before, .fa-kit-medical:before { content: "\f479" } .fa-person-through-window:before { content: "\e5a9" } .fa-toolbox:before { content: "\f552" } .fa-hands-holding-circle:before { content: "\e4fb" } .fa-bug:before { content: "\f188" } .fa-credit-card-alt:before, .fa-credit-card:before { content: "\f09d" } .fa-automobile:before, .fa-car:before { content: "\f1b9" } .fa-hand-holding-hand:before { content: "\e4f7" } .fa-book-open-reader:before, .fa-book-reader:before { content: "\f5da" } .fa-mountain-sun:before { content: "\e52f" } .fa-arrows-left-right-to-line:before { content: "\e4ba" } .fa-dice-d20:before { content: "\f6cf" } .fa-truck-droplet:before { content: "\e58c" } .fa-file-circle-xmark:before { content: "\e5a1" } .fa-temperature-arrow-up:before, .fa-temperature-up:before { content: "\e040" } .fa-medal:before { content: "\f5a2" } .fa-bed:before { content: "\f236" } .fa-h-square:before, .fa-square-h:before { content: "\f0fd" } .fa-podcast:before { content: "\f2ce" } .fa-temperature-4:before, .fa-temperature-full:before, .fa-thermometer-4:before, .fa-thermometer-full:before { content: "\f2c7" } .fa-bell:before { content: "\f0f3" } .fa-superscript:before { content: "\f12b" } .fa-plug-circle-xmark:before { content: "\e560" } .fa-star-of-life:before { content: "\f621" } .fa-phone-slash:before { content: "\f3dd" } .fa-paint-roller:before { content: "\f5aa" } .fa-hands-helping:before, .fa-handshake-angle:before { content: "\f4c4" } .fa-location-dot:before, .fa-map-marker-alt:before { content: "\f3c5" } .fa-file:before { content: "\f15b" } .fa-greater-than:before { content: "\3e" } .fa-person-swimming:before, .fa-swimmer:before { content: "\f5c4" } .fa-arrow-down:before { content: "\f063" } .fa-droplet:before, .fa-tint:before { content: "\f043" } .fa-eraser:before { content: "\f12d" } .fa-earth-america:before, .fa-earth-americas:before, .fa-earth:before, .fa-globe-americas:before { content: "\f57d" } .fa-person-burst:before { content: "\e53b" } .fa-dove:before { content: "\f4ba" } .fa-battery-0:before, .fa-battery-empty:before { content: "\f244" } .fa-socks:before { content: "\f696" } .fa-inbox:before { content: "\f01c" } .fa-section:before { content: "\e447" } .fa-gauge-high:before, .fa-tachometer-alt-fast:before, .fa-tachometer-alt:before { content: "\f625" } .fa-envelope-open-text:before { content: "\f658" } .fa-hospital-alt:before, .fa-hospital-wide:before, .fa-hospital:before { content: "\f0f8" } .fa-wine-bottle:before { content: "\f72f" } .fa-chess-rook:before { content: "\f447" } .fa-bars-staggered:before, .fa-reorder:before, .fa-stream:before { content: "\f550" } .fa-dharmachakra:before { content: "\f655" } .fa-hotdog:before { content: "\f80f" } .fa-blind:before, .fa-person-walking-with-cane:before { content: "\f29d" } .fa-drum:before { content: "\f569" } .fa-ice-cream:before { content: "\f810" } .fa-heart-circle-bolt:before { content: "\e4fc" } .fa-fax:before { content: "\f1ac" } .fa-paragraph:before { content: "\f1dd" } .fa-check-to-slot:before, .fa-vote-yea:before { content: "\f772" } .fa-star-half:before { content: "\f089" } .fa-boxes-alt:before, .fa-boxes-stacked:before, .fa-boxes:before { content: "\f468" } .fa-chain:before, .fa-link:before { content: "\f0c1" } .fa-assistive-listening-systems:before, .fa-ear-listen:before { content: "\f2a2" } .fa-tree-city:before { content: "\e587" } .fa-play:before { content: "\f04b" } .fa-font:before { content: "\f031" } .fa-rupiah-sign:before { content: "\e23d" } .fa-magnifying-glass:before, .fa-search:before { content: "\f002" } .fa-ping-pong-paddle-ball:before, .fa-table-tennis-paddle-ball:before, .fa-table-tennis:before { content: "\f45d" } .fa-diagnoses:before, .fa-person-dots-from-line:before { content: "\f470" } .fa-trash-can-arrow-up:before, .fa-trash-restore-alt:before { content: "\f82a" } .fa-naira-sign:before { content: "\e1f6" } .fa-cart-arrow-down:before { content: "\f218" } .fa-walkie-talkie:before { content: "\f8ef" } .fa-file-edit:before, .fa-file-pen:before { content: "\f31c" } .fa-receipt:before { content: "\f543" } .fa-pen-square:before, .fa-pencil-square:before, .fa-square-pen:before { content: "\f14b" } .fa-suitcase-rolling:before { content: "\f5c1" } .fa-person-circle-exclamation:before { content: "\e53f" } .fa-chevron-down:before { content: "\f078" } .fa-battery-5:before, .fa-battery-full:before, .fa-battery:before { content: "\f240" } .fa-skull-crossbones:before { content: "\f714" } .fa-code-compare:before { content: "\e13a" } .fa-list-dots:before, .fa-list-ul:before { content: "\f0ca" } .fa-school-lock:before { content: "\e56f" } .fa-tower-cell:before { content: "\e585" } .fa-down-long:before, .fa-long-arrow-alt-down:before { content: "\f309" } .fa-ranking-star:before { content: "\e561" } .fa-chess-king:before { content: "\f43f" } .fa-person-harassing:before { content: "\e549" } .fa-brazilian-real-sign:before { content: "\e46c" } .fa-landmark-alt:before, .fa-landmark-dome:before { content: "\f752" } .fa-arrow-up:before { content: "\f062" } .fa-television:before, .fa-tv-alt:before, .fa-tv:before { content: "\f26c" } .fa-shrimp:before { content: "\e448" } .fa-list-check:before, .fa-tasks:before { content: "\f0ae" } .fa-jug-detergent:before { content: "\e519" } .fa-circle-user:before, .fa-user-circle:before { content: "\f2bd" } .fa-user-shield:before { content: "\f505" } .fa-wind:before { content: "\f72e" } .fa-car-burst:before, .fa-car-crash:before { content: "\f5e1" } .fa-y:before { content: "\59" } .fa-person-snowboarding:before, .fa-snowboarding:before { content: "\f7ce" } .fa-shipping-fast:before, .fa-truck-fast:before { content: "\f48b" } .fa-fish:before { content: "\f578" } .fa-user-graduate:before { content: "\f501" } .fa-adjust:before, .fa-circle-half-stroke:before { content: "\f042" } .fa-clapperboard:before { content: "\e131" } .fa-circle-radiation:before, .fa-radiation-alt:before { content: "\f7ba" } .fa-baseball-ball:before, .fa-baseball:before { content: "\f433" } .fa-jet-fighter-up:before { content: "\e518" } .fa-diagram-project:before, .fa-project-diagram:before { content: "\f542" } .fa-copy:before { content: "\f0c5" } .fa-volume-mute:before, .fa-volume-times:before, .fa-volume-xmark:before { content: "\f6a9" } .fa-hand-sparkles:before { content: "\e05d" } .fa-grip-horizontal:before, .fa-grip:before { content: "\f58d" } .fa-share-from-square:before, .fa-share-square:before { content: "\f14d" } .fa-gun:before { content: "\e19b" } .fa-phone-square:before, .fa-square-phone:before { content: "\f098" } .fa-add:before, .fa-plus:before { content: "\2b" } .fa-expand:before { content: "\f065" } .fa-computer:before { content: "\e4e5" } .fa-close:before, .fa-multiply:before, .fa-remove:before, .fa-times:before, .fa-xmark:before { content: "\f00d" } .fa-arrows-up-down-left-right:before, .fa-arrows:before { content: "\f047" } .fa-chalkboard-teacher:before, .fa-chalkboard-user:before { content: "\f51c" } .fa-peso-sign:before { content: "\e222" } .fa-building-shield:before { content: "\e4d8" } .fa-baby:before { content: "\f77c" } .fa-users-line:before { content: "\e592" } .fa-quote-left-alt:before, .fa-quote-left:before { content: "\f10d" } .fa-tractor:before { content: "\f722" } .fa-trash-arrow-up:before, .fa-trash-restore:before { content: "\f829" } .fa-arrow-down-up-lock:before { content: "\e4b0" } .fa-lines-leaning:before { content: "\e51e" } .fa-ruler-combined:before { content: "\f546" } .fa-copyright:before { content: "\f1f9" } .fa-equals:before { content: "\3d" } .fa-blender:before { content: "\f517" } .fa-teeth:before { content: "\f62e" } .fa-ils:before, .fa-shekel-sign:before, .fa-shekel:before, .fa-sheqel-sign:before, .fa-sheqel:before { content: "\f20b" } .fa-map:before { content: "\f279" } .fa-rocket:before { content: "\f135" } .fa-photo-film:before, .fa-photo-video:before { content: "\f87c" } .fa-folder-minus:before { content: "\f65d" } .fa-store:before { content: "\f54e" } .fa-arrow-trend-up:before { content: "\e098" } .fa-plug-circle-minus:before { content: "\e55e" } .fa-sign-hanging:before, .fa-sign:before { content: "\f4d9" } .fa-bezier-curve:before { content: "\f55b" } .fa-bell-slash:before { content: "\f1f6" } .fa-tablet-android:before, .fa-tablet:before { content: "\f3fb" } .fa-school-flag:before { content: "\e56e" } .fa-fill:before { content: "\f575" } .fa-angle-up:before { content: "\f106" } .fa-drumstick-bite:before { content: "\f6d7" } .fa-holly-berry:before { content: "\f7aa" } .fa-chevron-left:before { content: "\f053" } .fa-bacteria:before { content: "\e059" } .fa-hand-lizard:before { content: "\f258" } .fa-disease:before { content: "\f7fa" } .fa-briefcase-medical:before { content: "\f469" } .fa-genderless:before { content: "\f22d" } .fa-chevron-right:before { content: "\f054" } .fa-retweet:before { content: "\f079" } .fa-car-alt:before, .fa-car-rear:before { content: "\f5de" } .fa-pump-soap:before { content: "\e06b" } .fa-video-slash:before { content: "\f4e2" } .fa-battery-2:before, .fa-battery-quarter:before { content: "\f243" } .fa-radio:before { content: "\f8d7" } .fa-baby-carriage:before, .fa-carriage-baby:before { content: "\f77d" } .fa-traffic-light:before { content: "\f637" } .fa-thermometer:before { content: "\f491" } .fa-vr-cardboard:before { content: "\f729" } .fa-hand-middle-finger:before { content: "\f806" } .fa-percent:before, .fa-percentage:before { content: "\25" } .fa-truck-moving:before { content: "\f4df" } .fa-glass-water-droplet:before { content: "\e4f5" } .fa-display:before { content: "\e163" } .fa-face-smile:before, .fa-smile:before { content: "\f118" } .fa-thumb-tack:before, .fa-thumbtack:before { content: "\f08d" } .fa-trophy:before { content: "\f091" } .fa-person-praying:before, .fa-pray:before { content: "\f683" } .fa-hammer:before { content: "\f6e3" } .fa-hand-peace:before { content: "\f25b" } .fa-rotate:before, .fa-sync-alt:before { content: "\f2f1" } .fa-spinner:before { content: "\f110" } .fa-robot:before { content: "\f544" } .fa-peace:before { content: "\f67c" } .fa-cogs:before, .fa-gears:before { content: "\f085" } .fa-warehouse:before { content: "\f494" } .fa-arrow-up-right-dots:before { content: "\e4b7" } .fa-splotch:before { content: "\f5bc" } .fa-face-grin-hearts:before, .fa-grin-hearts:before { content: "\f584" } .fa-dice-four:before { content: "\f524" } .fa-sim-card:before { content: "\f7c4" } .fa-transgender-alt:before, .fa-transgender:before { content: "\f225" } .fa-mercury:before { content: "\f223" } .fa-arrow-turn-down:before, .fa-level-down:before { content: "\f149" } .fa-person-falling-burst:before { content: "\e547" } .fa-award:before { content: "\f559" } .fa-ticket-alt:before, .fa-ticket-simple:before { content: "\f3ff" } .fa-building:before { content: "\f1ad" } .fa-angle-double-left:before, .fa-angles-left:before { content: "\f100" } .fa-qrcode:before { content: "\f029" } .fa-clock-rotate-left:before, .fa-history:before { content: "\f1da" } .fa-face-grin-beam-sweat:before, .fa-grin-beam-sweat:before { content: "\f583" } .fa-arrow-right-from-file:before, .fa-file-export:before { content: "\f56e" } .fa-shield-blank:before, .fa-shield:before { content: "\f132" } .fa-arrow-up-short-wide:before, .fa-sort-amount-up-alt:before { content: "\f885" } .fa-house-medical:before { content: "\e3b2" } .fa-golf-ball-tee:before, .fa-golf-ball:before { content: "\f450" } .fa-chevron-circle-left:before, .fa-circle-chevron-left:before { content: "\f137" } .fa-house-chimney-window:before { content: "\e00d" } .fa-pen-nib:before { content: "\f5ad" } .fa-tent-arrow-turn-left:before { content: "\e580" } .fa-tents:before { content: "\e582" } .fa-magic:before, .fa-wand-magic:before { content: "\f0d0" } .fa-dog:before { content: "\f6d3" } .fa-carrot:before { content: "\f787" } .fa-moon:before { content: "\f186" } .fa-wine-glass-alt:before, .fa-wine-glass-empty:before { content: "\f5ce" } .fa-cheese:before { content: "\f7ef" } .fa-yin-yang:before { content: "\f6ad" } .fa-music:before { content: "\f001" } .fa-code-commit:before { content: "\f386" } .fa-temperature-low:before { content: "\f76b" } .fa-biking:before, .fa-person-biking:before { content: "\f84a" } .fa-broom:before { content: "\f51a" } .fa-shield-heart:before { content: "\e574" } .fa-gopuram:before { content: "\f664" } .fa-earth-oceania:before, .fa-globe-oceania:before { content: "\e47b" } .fa-square-xmark:before, .fa-times-square:before, .fa-xmark-square:before { content: "\f2d3" } .fa-hashtag:before { content: "\23" } .fa-expand-alt:before, .fa-up-right-and-down-left-from-center:before { content: "\f424" } .fa-oil-can:before { content: "\f613" } .fa-t:before { content: "\54" } .fa-hippo:before { content: "\f6ed" } .fa-chart-column:before { content: "\e0e3" } .fa-infinity:before { content: "\f534" } .fa-vial-circle-check:before { content: "\e596" } .fa-person-arrow-down-to-line:before { content: "\e538" } .fa-voicemail:before { content: "\f897" } .fa-fan:before { content: "\f863" } .fa-person-walking-luggage:before { content: "\e554" } .fa-arrows-alt-v:before, .fa-up-down:before { content: "\f338" } .fa-cloud-moon-rain:before { content: "\f73c" } .fa-calendar:before { content: "\f133" } .fa-trailer:before { content: "\e041" } .fa-bahai:before, .fa-haykal:before { content: "\f666" } .fa-sd-card:before { content: "\f7c2" } .fa-dragon:before { content: "\f6d5" } .fa-shoe-prints:before { content: "\f54b" } .fa-circle-plus:before, .fa-plus-circle:before { content: "\f055" } .fa-face-grin-tongue-wink:before, .fa-grin-tongue-wink:before { content: "\f58b" } .fa-hand-holding:before { content: "\f4bd" } .fa-plug-circle-exclamation:before { content: "\e55d" } .fa-chain-broken:before, .fa-chain-slash:before, .fa-link-slash:before, .fa-unlink:before { content: "\f127" } .fa-clone:before { content: "\f24d" } .fa-person-walking-arrow-loop-left:before { content: "\e551" } .fa-arrow-up-z-a:before, .fa-sort-alpha-up-alt:before { content: "\f882" } .fa-fire-alt:before, .fa-fire-flame-curved:before { content: "\f7e4" } .fa-tornado:before { content: "\f76f" } .fa-file-circle-plus:before { content: "\e494" } .fa-book-quran:before, .fa-quran:before { content: "\f687" } .fa-anchor:before { content: "\f13d" } .fa-border-all:before { content: "\f84c" } .fa-angry:before, .fa-face-angry:before { content: "\f556" } .fa-cookie-bite:before { content: "\f564" } .fa-arrow-trend-down:before { content: "\e097" } .fa-feed:before, .fa-rss:before { content: "\f09e" } .fa-draw-polygon:before { content: "\f5ee" } .fa-balance-scale:before, .fa-scale-balanced:before { content: "\f24e" } .fa-gauge-simple-high:before, .fa-tachometer-fast:before, .fa-tachometer:before { content: "\f62a" } .fa-shower:before { content: "\f2cc" } .fa-desktop-alt:before, .fa-desktop:before { content: "\f390" } .fa-m:before { content: "\4d" } .fa-table-list:before, .fa-th-list:before { content: "\f00b" } .fa-comment-sms:before, .fa-sms:before { content: "\f7cd" } .fa-book:before { content: "\f02d" } .fa-user-plus:before { content: "\f234" } .fa-check:before { content: "\f00c" } .fa-battery-4:before, .fa-battery-three-quarters:before { content: "\f241" } .fa-house-circle-check:before { content: "\e509" } .fa-angle-left:before { content: "\f104" } .fa-diagram-successor:before { content: "\e47a" } .fa-truck-arrow-right:before { content: "\e58b" } .fa-arrows-split-up-and-left:before { content: "\e4bc" } .fa-fist-raised:before, .fa-hand-fist:before { content: "\f6de" } .fa-cloud-moon:before { content: "\f6c3" } .fa-briefcase:before { content: "\f0b1" } .fa-person-falling:before { content: "\e546" } .fa-image-portrait:before, .fa-portrait:before { content: "\f3e0" } .fa-user-tag:before { content: "\f507" } .fa-rug:before { content: "\e569" } .fa-earth-europe:before, .fa-globe-europe:before { content: "\f7a2" } .fa-cart-flatbed-suitcase:before, .fa-luggage-cart:before { content: "\f59d" } .fa-rectangle-times:before, .fa-rectangle-xmark:before, .fa-times-rectangle:before, .fa-window-close:before { content: "\f410" } .fa-baht-sign:before { content: "\e0ac" } .fa-book-open:before { content: "\f518" } .fa-book-journal-whills:before, .fa-journal-whills:before { content: "\f66a" } .fa-handcuffs:before { content: "\e4f8" } .fa-exclamation-triangle:before, .fa-triangle-exclamation:before, .fa-warning:before { content: "\f071" } .fa-database:before { content: "\f1c0" } .fa-arrow-turn-right:before, .fa-mail-forward:before, .fa-share:before { content: "\f064" } .fa-bottle-droplet:before { content: "\e4c4" } .fa-mask-face:before { content: "\e1d7" } .fa-hill-rockslide:before { content: "\e508" } .fa-exchange-alt:before, .fa-right-left:before { content: "\f362" } .fa-paper-plane:before { content: "\f1d8" } .fa-road-circle-exclamation:before { content: "\e565" } .fa-dungeon:before { content: "\f6d9" } .fa-align-right:before { content: "\f038" } .fa-money-bill-1-wave:before, .fa-money-bill-wave-alt:before { content: "\f53b" } .fa-life-ring:before { content: "\f1cd" } .fa-hands:before, .fa-sign-language:before, .fa-signing:before { content: "\f2a7" } .fa-calendar-day:before { content: "\f783" } .fa-ladder-water:before, .fa-swimming-pool:before, .fa-water-ladder:before { content: "\f5c5" } .fa-arrows-up-down:before, .fa-arrows-v:before { content: "\f07d" } .fa-face-grimace:before, .fa-grimace:before { content: "\f57f" } .fa-wheelchair-alt:before, .fa-wheelchair-move:before { content: "\e2ce" } .fa-level-down-alt:before, .fa-turn-down:before { content: "\f3be" } .fa-person-walking-arrow-right:before { content: "\e552" } .fa-envelope-square:before, .fa-square-envelope:before { content: "\f199" } .fa-dice:before { content: "\f522" } .fa-bowling-ball:before { content: "\f436" } .fa-brain:before { content: "\f5dc" } .fa-band-aid:before, .fa-bandage:before { content: "\f462" } .fa-calendar-minus:before { content: "\f272" } .fa-circle-xmark:before, .fa-times-circle:before, .fa-xmark-circle:before { content: "\f057" } .fa-gifts:before { content: "\f79c" } .fa-hotel:before { content: "\f594" } .fa-earth-asia:before, .fa-globe-asia:before { content: "\f57e" } .fa-id-card-alt:before, .fa-id-card-clip:before { content: "\f47f" } .fa-magnifying-glass-plus:before, .fa-search-plus:before { content: "\f00e" } .fa-thumbs-up:before { content: "\f164" } .fa-user-clock:before { content: "\f4fd" } .fa-allergies:before, .fa-hand-dots:before { content: "\f461" } .fa-file-invoice:before { content: "\f570" } .fa-window-minimize:before { content: "\f2d1" } .fa-coffee:before, .fa-mug-saucer:before { content: "\f0f4" } .fa-brush:before { content: "\f55d" } .fa-mask:before { content: "\f6fa" } .fa-magnifying-glass-minus:before, .fa-search-minus:before { content: "\f010" } .fa-ruler-vertical:before { content: "\f548" } .fa-user-alt:before, .fa-user-large:before { content: "\f406" } .fa-train-tram:before { content: "\e5b4" } .fa-user-nurse:before { content: "\f82f" } .fa-syringe:before { content: "\f48e" } .fa-cloud-sun:before { content: "\f6c4" } .fa-stopwatch-20:before { content: "\e06f" } .fa-square-full:before { content: "\f45c" } .fa-magnet:before { content: "\f076" } .fa-jar:before { content: "\e516" } .fa-note-sticky:before, .fa-sticky-note:before { content: "\f249" } .fa-bug-slash:before { content: "\e490" } .fa-arrow-up-from-water-pump:before { content: "\e4b6" } .fa-bone:before { content: "\f5d7" } .fa-user-injured:before { content: "\f728" } .fa-face-sad-tear:before, .fa-sad-tear:before { content: "\f5b4" } .fa-plane:before { content: "\f072" } .fa-tent-arrows-down:before { content: "\e581" } .fa-exclamation:before { content: "\21" } .fa-arrows-spin:before { content: "\e4bb" } .fa-print:before { content: "\f02f" } .fa-try:before, .fa-turkish-lira-sign:before, .fa-turkish-lira:before { content: "\e2bb" } .fa-dollar-sign:before, .fa-dollar:before, .fa-usd:before { content: "\24" } .fa-x:before { content: "\58" } .fa-magnifying-glass-dollar:before, .fa-search-dollar:before { content: "\f688" } .fa-users-cog:before, .fa-users-gear:before { content: "\f509" } .fa-person-military-pointing:before { content: "\e54a" } .fa-bank:before, .fa-building-columns:before, .fa-institution:before, .fa-museum:before, .fa-university:before { content: "\f19c" } .fa-umbrella:before { content: "\f0e9" } .fa-trowel:before { content: "\e589" } .fa-d:before { content: "\44" } .fa-stapler:before { content: "\e5af" } .fa-masks-theater:before, .fa-theater-masks:before { content: "\f630" } .fa-kip-sign:before { content: "\e1c4" } .fa-hand-point-left:before { content: "\f0a5" } .fa-handshake-alt:before, .fa-handshake-simple:before { content: "\f4c6" } .fa-fighter-jet:before, .fa-jet-fighter:before { content: "\f0fb" } .fa-share-alt-square:before, .fa-square-share-nodes:before { content: "\f1e1" } .fa-barcode:before { content: "\f02a" } .fa-plus-minus:before { content: "\e43c" } .fa-video-camera:before, .fa-video:before { content: "\f03d" } .fa-graduation-cap:before, .fa-mortar-board:before { content: "\f19d" } .fa-hand-holding-medical:before { content: "\e05c" } .fa-person-circle-check:before { content: "\e53e" } .fa-level-up-alt:before, .fa-turn-up:before { content: "\f3bf" } .fa-sr-only, .fa-sr-only-focusable:not(:focus), .sr-only, .sr-only-focusable:not(:focus) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border-width: 0 } :host, :root { --fa-style-family-brands: "Font Awesome 6 Brands"; --fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands" } @font-face { font-family: "Font Awesome 6 Brands"; font-style: normal; font-weight: 400; font-display: block; src: url(./fa-brands-400.woff2) format("woff2"), url(./fa-brands-400.ttf) format("truetype") } .fa-brands, .fab { font-weight: 400 } .fa-monero:before { content: "\f3d0" } .fa-hooli:before { content: "\f427" } .fa-yelp:before { content: "\f1e9" } .fa-cc-visa:before { content: "\f1f0" } .fa-lastfm:before { content: "\f202" } .fa-shopware:before { content: "\f5b5" } .fa-creative-commons-nc:before { content: "\f4e8" } .fa-aws:before { content: "\f375" } .fa-redhat:before { content: "\f7bc" } .fa-yoast:before { content: "\f2b1" } .fa-cloudflare:before { content: "\e07d" } .fa-ups:before { content: "\f7e0" } .fa-wpexplorer:before { content: "\f2de" } .fa-dyalog:before { content: "\f399" } .fa-bity:before { content: "\f37a" } .fa-stackpath:before { content: "\f842" } .fa-buysellads:before { content: "\f20d" } .fa-first-order:before { content: "\f2b0" } .fa-modx:before { content: "\f285" } .fa-guilded:before { content: "\e07e" } .fa-vnv:before { content: "\f40b" } .fa-js-square:before, .fa-square-js:before { content: "\f3b9" } .fa-microsoft:before { content: "\f3ca" } .fa-qq:before { content: "\f1d6" } .fa-orcid:before { content: "\f8d2" } .fa-java:before { content: "\f4e4" } .fa-invision:before { content: "\f7b0" } .fa-creative-commons-pd-alt:before { content: "\f4ed" } .fa-centercode:before { content: "\f380" } .fa-glide-g:before { content: "\f2a6" } .fa-drupal:before { content: "\f1a9" } .fa-hire-a-helper:before { content: "\f3b0" } .fa-creative-commons-by:before { content: "\f4e7" } .fa-unity:before { content: "\e049" } .fa-whmcs:before { content: "\f40d" } .fa-rocketchat:before { content: "\f3e8" } .fa-vk:before { content: "\f189" } .fa-untappd:before { content: "\f405" } .fa-mailchimp:before { content: "\f59e" } .fa-css3-alt:before { content: "\f38b" } .fa-reddit-square:before, .fa-square-reddit:before { content: "\f1a2" } .fa-vimeo-v:before { content: "\f27d" } .fa-contao:before { content: "\f26d" } .fa-square-font-awesome:before { content: "\e5ad" } .fa-deskpro:before { content: "\f38f" } .fa-sistrix:before { content: "\f3ee" } .fa-instagram-square:before, .fa-square-instagram:before { content: "\e055" } .fa-battle-net:before { content: "\f835" } .fa-the-red-yeti:before { content: "\f69d" } .fa-hacker-news-square:before, .fa-square-hacker-news:before { content: "\f3af" } .fa-edge:before { content: "\f282" } .fa-napster:before { content: "\f3d2" } .fa-snapchat-square:before, .fa-square-snapchat:before { content: "\f2ad" } .fa-google-plus-g:before { content: "\f0d5" } .fa-artstation:before { content: "\f77a" } .fa-markdown:before { content: "\f60f" } .fa-sourcetree:before { content: "\f7d3" } .fa-google-plus:before { content: "\f2b3" } .fa-diaspora:before { content: "\f791" } .fa-foursquare:before { content: "\f180" } .fa-stack-overflow:before { content: "\f16c" } .fa-github-alt:before { content: "\f113" } .fa-phoenix-squadron:before { content: "\f511" } .fa-pagelines:before { content: "\f18c" } .fa-algolia:before { content: "\f36c" } .fa-red-river:before { content: "\f3e3" } .fa-creative-commons-sa:before { content: "\f4ef" } .fa-safari:before { content: "\f267" } .fa-google:before { content: "\f1a0" } .fa-font-awesome-alt:before, .fa-square-font-awesome-stroke:before { content: "\f35c" } .fa-atlassian:before { content: "\f77b" } .fa-linkedin-in:before { content: "\f0e1" } .fa-digital-ocean:before { content: "\f391" } .fa-nimblr:before { content: "\f5a8" } .fa-chromecast:before { content: "\f838" } .fa-evernote:before { content: "\f839" } .fa-hacker-news:before { content: "\f1d4" } .fa-creative-commons-sampling:before { content: "\f4f0" } .fa-adversal:before { content: "\f36a" } .fa-creative-commons:before { content: "\f25e" } .fa-watchman-monitoring:before { content: "\e087" } .fa-fonticons:before { content: "\f280" } .fa-weixin:before { content: "\f1d7" } .fa-shirtsinbulk:before { content: "\f214" } .fa-codepen:before { content: "\f1cb" } .fa-git-alt:before { content: "\f841" } .fa-lyft:before { content: "\f3c3" } .fa-rev:before { content: "\f5b2" } .fa-windows:before { content: "\f17a" } .fa-wizards-of-the-coast:before { content: "\f730" } .fa-square-viadeo:before, .fa-viadeo-square:before { content: "\f2aa" } .fa-meetup:before { content: "\f2e0" } .fa-centos:before { content: "\f789" } .fa-adn:before { content: "\f170" } .fa-cloudsmith:before { content: "\f384" } .fa-pied-piper-alt:before { content: "\f1a8" } .fa-dribbble-square:before, .fa-square-dribbble:before { content: "\f397" } .fa-codiepie:before { content: "\f284" } .fa-node:before { content: "\f419" } .fa-mix:before { content: "\f3cb" } .fa-steam:before { content: "\f1b6" } .fa-cc-apple-pay:before { content: "\f416" } .fa-scribd:before { content: "\f28a" } .fa-openid:before { content: "\f19b" } .fa-instalod:before { content: "\e081" } .fa-expeditedssl:before { content: "\f23e" } .fa-sellcast:before { content: "\f2da" } .fa-square-twitter:before, .fa-twitter-square:before { content: "\f081" } .fa-r-project:before { content: "\f4f7" } .fa-delicious:before { content: "\f1a5" } .fa-freebsd:before { content: "\f3a4" } .fa-vuejs:before { content: "\f41f" } .fa-accusoft:before { content: "\f369" } .fa-ioxhost:before { content: "\f208" } .fa-fonticons-fi:before { content: "\f3a2" } .fa-app-store:before { content: "\f36f" } .fa-cc-mastercard:before { content: "\f1f1" } .fa-itunes-note:before { content: "\f3b5" } .fa-golang:before { content: "\e40f" } .fa-kickstarter:before { content: "\f3bb" } .fa-grav:before { content: "\f2d6" } .fa-weibo:before { content: "\f18a" } .fa-uncharted:before { content: "\e084" } .fa-firstdraft:before { content: "\f3a1" } .fa-square-youtube:before, .fa-youtube-square:before { content: "\f431" } .fa-wikipedia-w:before { content: "\f266" } .fa-rendact:before, .fa-wpressr:before { content: "\f3e4" } .fa-angellist:before { content: "\f209" } .fa-galactic-republic:before { content: "\f50c" } .fa-nfc-directional:before { content: "\e530" } .fa-skype:before { content: "\f17e" } .fa-joget:before { content: "\f3b7" } .fa-fedora:before { content: "\f798" } .fa-stripe-s:before { content: "\f42a" } .fa-meta:before { content: "\e49b" } .fa-laravel:before { content: "\f3bd" } .fa-hotjar:before { content: "\f3b1" } .fa-bluetooth-b:before { content: "\f294" } .fa-sticker-mule:before { content: "\f3f7" } .fa-creative-commons-zero:before { content: "\f4f3" } .fa-hips:before { content: "\f452" } .fa-behance:before { content: "\f1b4" } .fa-reddit:before { content: "\f1a1" } .fa-discord:before { content: "\f392" } .fa-chrome:before { content: "\f268" } .fa-app-store-ios:before { content: "\f370" } .fa-cc-discover:before { content: "\f1f2" } .fa-wpbeginner:before { content: "\f297" } .fa-confluence:before { content: "\f78d" } .fa-mdb:before { content: "\f8ca" } .fa-dochub:before { content: "\f394" } .fa-accessible-icon:before { content: "\f368" } .fa-ebay:before { content: "\f4f4" } .fa-amazon:before { content: "\f270" } .fa-unsplash:before { content: "\e07c" } .fa-yarn:before { content: "\f7e3" } .fa-square-steam:before, .fa-steam-square:before { content: "\f1b7" } .fa-500px:before { content: "\f26e" } .fa-square-vimeo:before, .fa-vimeo-square:before { content: "\f194" } .fa-asymmetrik:before { content: "\f372" } .fa-font-awesome-flag:before, .fa-font-awesome-logo-full:before, .fa-font-awesome:before { content: "\f2b4" } .fa-gratipay:before { content: "\f184" } .fa-apple:before { content: "\f179" } .fa-hive:before { content: "\e07f" } .fa-gitkraken:before { content: "\f3a6" } .fa-keybase:before { content: "\f4f5" } .fa-apple-pay:before { content: "\f415" } .fa-padlet:before { content: "\e4a0" } .fa-amazon-pay:before { content: "\f42c" } .fa-github-square:before, .fa-square-github:before { content: "\f092" } .fa-stumbleupon:before { content: "\f1a4" } .fa-fedex:before { content: "\f797" } .fa-phoenix-framework:before { content: "\f3dc" } .fa-shopify:before { content: "\e057" } .fa-neos:before { content: "\f612" } .fa-hackerrank:before { content: "\f5f7" } .fa-researchgate:before { content: "\f4f8" } .fa-swift:before { content: "\f8e1" } .fa-angular:before { content: "\f420" } .fa-speakap:before { content: "\f3f3" } .fa-angrycreative:before { content: "\f36e" } .fa-y-combinator:before { content: "\f23b" } .fa-empire:before { content: "\f1d1" } .fa-envira:before { content: "\f299" } .fa-gitlab-square:before, .fa-square-gitlab:before { content: "\e5ae" } .fa-studiovinari:before { content: "\f3f8" } .fa-pied-piper:before { content: "\f2ae" } .fa-wordpress:before { content: "\f19a" } .fa-product-hunt:before { content: "\f288" } .fa-firefox:before { content: "\f269" } .fa-linode:before { content: "\f2b8" } .fa-goodreads:before { content: "\f3a8" } .fa-odnoklassniki-square:before, .fa-square-odnoklassniki:before { content: "\f264" } .fa-jsfiddle:before { content: "\f1cc" } .fa-sith:before { content: "\f512" } .fa-themeisle:before { content: "\f2b2" } .fa-page4:before { content: "\f3d7" } .fa-hashnode:before { content: "\e499" } .fa-react:before { content: "\f41b" } .fa-cc-paypal:before { content: "\f1f4" } .fa-squarespace:before { content: "\f5be" } .fa-cc-stripe:before { content: "\f1f5" } .fa-creative-commons-share:before { content: "\f4f2" } .fa-bitcoin:before { content: "\f379" } .fa-keycdn:before { content: "\f3ba" } .fa-opera:before { content: "\f26a" } .fa-itch-io:before { content: "\f83a" } .fa-umbraco:before { content: "\f8e8" } .fa-galactic-senate:before { content: "\f50d" } .fa-ubuntu:before { content: "\f7df" } .fa-draft2digital:before { content: "\f396" } .fa-stripe:before { content: "\f429" } .fa-houzz:before { content: "\f27c" } .fa-gg:before { content: "\f260" } .fa-dhl:before { content: "\f790" } .fa-pinterest-square:before, .fa-square-pinterest:before { content: "\f0d3" } .fa-xing:before { content: "\f168" } .fa-blackberry:before { content: "\f37b" } .fa-creative-commons-pd:before { content: "\f4ec" } .fa-playstation:before { content: "\f3df" } .fa-quinscape:before { content: "\f459" } .fa-less:before { content: "\f41d" } .fa-blogger-b:before { content: "\f37d" } .fa-opencart:before { content: "\f23d" } .fa-vine:before { content: "\f1ca" } .fa-paypal:before { content: "\f1ed" } .fa-gitlab:before { content: "\f296" } .fa-typo3:before { content: "\f42b" } .fa-reddit-alien:before { content: "\f281" } .fa-yahoo:before { content: "\f19e" } .fa-dailymotion:before { content: "\e052" } .fa-affiliatetheme:before { content: "\f36b" } .fa-pied-piper-pp:before { content: "\f1a7" } .fa-bootstrap:before { content: "\f836" } .fa-odnoklassniki:before { content: "\f263" } .fa-nfc-symbol:before { content: "\e531" } .fa-ethereum:before { content: "\f42e" } .fa-speaker-deck:before { content: "\f83c" } .fa-creative-commons-nc-eu:before { content: "\f4e9" } .fa-patreon:before { content: "\f3d9" } .fa-avianex:before { content: "\f374" } .fa-ello:before { content: "\f5f1" } .fa-gofore:before { content: "\f3a7" } .fa-bimobject:before { content: "\f378" } .fa-facebook-f:before { content: "\f39e" } .fa-google-plus-square:before, .fa-square-google-plus:before { content: "\f0d4" } .fa-mandalorian:before { content: "\f50f" } .fa-first-order-alt:before { content: "\f50a" } .fa-osi:before { content: "\f41a" } .fa-google-wallet:before { content: "\f1ee" } .fa-d-and-d-beyond:before { content: "\f6ca" } .fa-periscope:before { content: "\f3da" } .fa-fulcrum:before { content: "\f50b" } .fa-cloudscale:before { content: "\f383" } .fa-forumbee:before { content: "\f211" } .fa-mizuni:before { content: "\f3cc" } .fa-schlix:before { content: "\f3ea" } .fa-square-xing:before, .fa-xing-square:before { content: "\f169" } .fa-bandcamp:before { content: "\f2d5" } .fa-wpforms:before { content: "\f298" } .fa-cloudversify:before { content: "\f385" } .fa-usps:before { content: "\f7e1" } .fa-megaport:before { content: "\f5a3" } .fa-magento:before { content: "\f3c4" } .fa-spotify:before { content: "\f1bc" } .fa-optin-monster:before { content: "\f23c" } .fa-fly:before { content: "\f417" } .fa-aviato:before { content: "\f421" } .fa-itunes:before { content: "\f3b4" } .fa-cuttlefish:before { content: "\f38c" } .fa-blogger:before { content: "\f37c" } .fa-flickr:before { content: "\f16e" } .fa-viber:before { content: "\f409" } .fa-soundcloud:before { content: "\f1be" } .fa-digg:before { content: "\f1a6" } .fa-tencent-weibo:before { content: "\f1d5" } .fa-symfony:before { content: "\f83d" } .fa-maxcdn:before { content: "\f136" } .fa-etsy:before { content: "\f2d7" } .fa-facebook-messenger:before { content: "\f39f" } .fa-audible:before { content: "\f373" } .fa-think-peaks:before { content: "\f731" } .fa-bilibili:before { content: "\e3d9" } .fa-erlang:before { content: "\f39d" } .fa-cotton-bureau:before { content: "\f89e" } .fa-dashcube:before { content: "\f210" } .fa-42-group:before, .fa-innosoft:before { content: "\e080" } .fa-stack-exchange:before { content: "\f18d" } .fa-elementor:before { content: "\f430" } .fa-pied-piper-square:before, .fa-square-pied-piper:before { content: "\e01e" } .fa-creative-commons-nd:before { content: "\f4eb" } .fa-palfed:before { content: "\f3d8" } .fa-superpowers:before { content: "\f2dd" } .fa-resolving:before { content: "\f3e7" } .fa-xbox:before { content: "\f412" } .fa-searchengin:before { content: "\f3eb" } .fa-tiktok:before { content: "\e07b" } .fa-facebook-square:before, .fa-square-facebook:before { content: "\f082" } .fa-renren:before { content: "\f18b" } .fa-linux:before { content: "\f17c" } .fa-glide:before { content: "\f2a5" } .fa-linkedin:before { content: "\f08c" } .fa-hubspot:before { content: "\f3b2" } .fa-deploydog:before { content: "\f38e" } .fa-twitch:before { content: "\f1e8" } .fa-ravelry:before { content: "\f2d9" } .fa-mixer:before { content: "\e056" } .fa-lastfm-square:before, .fa-square-lastfm:before { content: "\f203" } .fa-vimeo:before { content: "\f40a" } .fa-mendeley:before { content: "\f7b3" } .fa-uniregistry:before { content: "\f404" } .fa-figma:before { content: "\f799" } .fa-creative-commons-remix:before { content: "\f4ee" } .fa-cc-amazon-pay:before { content: "\f42d" } .fa-dropbox:before { content: "\f16b" } .fa-instagram:before { content: "\f16d" } .fa-cmplid:before { content: "\e360" } .fa-facebook:before { content: "\f09a" } .fa-gripfire:before { content: "\f3ac" } .fa-jedi-order:before { content: "\f50e" } .fa-uikit:before { content: "\f403" } .fa-fort-awesome-alt:before { content: "\f3a3" } .fa-phabricator:before { content: "\f3db" } .fa-ussunnah:before { content: "\f407" } .fa-earlybirds:before { content: "\f39a" } .fa-trade-federation:before { content: "\f513" } .fa-autoprefixer:before { content: "\f41c" } .fa-whatsapp:before { content: "\f232" } .fa-slideshare:before { content: "\f1e7" } .fa-google-play:before { content: "\f3ab" } .fa-viadeo:before { content: "\f2a9" } .fa-line:before { content: "\f3c0" } .fa-google-drive:before { content: "\f3aa" } .fa-servicestack:before { content: "\f3ec" } .fa-simplybuilt:before { content: "\f215" } .fa-bitbucket:before { content: "\f171" } .fa-imdb:before { content: "\f2d8" } .fa-deezer:before { content: "\e077" } .fa-raspberry-pi:before { content: "\f7bb" } .fa-jira:before { content: "\f7b1" } .fa-docker:before { content: "\f395" } .fa-screenpal:before { content: "\e570" } .fa-bluetooth:before { content: "\f293" } .fa-gitter:before { content: "\f426" } .fa-d-and-d:before { content: "\f38d" } .fa-microblog:before { content: "\e01a" } .fa-cc-diners-club:before { content: "\f24c" } .fa-gg-circle:before { content: "\f261" } .fa-pied-piper-hat:before { content: "\f4e5" } .fa-kickstarter-k:before { content: "\f3bc" } .fa-yandex:before { content: "\f413" } .fa-readme:before { content: "\f4d5" } .fa-html5:before { content: "\f13b" } .fa-sellsy:before { content: "\f213" } .fa-sass:before { content: "\f41e" } .fa-wirsindhandwerk:before, .fa-wsh:before { content: "\e2d0" } .fa-buromobelexperte:before { content: "\f37f" } .fa-salesforce:before { content: "\f83b" } .fa-octopus-deploy:before { content: "\e082" } .fa-medapps:before { content: "\f3c6" } .fa-ns8:before { content: "\f3d5" } .fa-pinterest-p:before { content: "\f231" } .fa-apper:before { content: "\f371" } .fa-fort-awesome:before { content: "\f286" } .fa-waze:before { content: "\f83f" } .fa-cc-jcb:before { content: "\f24b" } .fa-snapchat-ghost:before, .fa-snapchat:before { content: "\f2ab" } .fa-fantasy-flight-games:before { content: "\f6dc" } .fa-rust:before { content: "\e07a" } .fa-wix:before { content: "\f5cf" } .fa-behance-square:before, .fa-square-behance:before { content: "\f1b5" } .fa-supple:before { content: "\f3f9" } .fa-rebel:before { content: "\f1d0" } .fa-css3:before { content: "\f13c" } .fa-staylinked:before { content: "\f3f5" } .fa-kaggle:before { content: "\f5fa" } .fa-space-awesome:before { content: "\e5ac" } .fa-deviantart:before { content: "\f1bd" } .fa-cpanel:before { content: "\f388" } .fa-goodreads-g:before { content: "\f3a9" } .fa-git-square:before, .fa-square-git:before { content: "\f1d2" } .fa-square-tumblr:before, .fa-tumblr-square:before { content: "\f174" } .fa-trello:before { content: "\f181" } .fa-creative-commons-nc-jp:before { content: "\f4ea" } .fa-get-pocket:before { content: "\f265" } .fa-perbyte:before { content: "\e083" } .fa-grunt:before { content: "\f3ad" } .fa-weebly:before { content: "\f5cc" } .fa-connectdevelop:before { content: "\f20e" } .fa-leanpub:before { content: "\f212" } .fa-black-tie:before { content: "\f27e" } .fa-themeco:before { content: "\f5c6" } .fa-python:before { content: "\f3e2" } .fa-android:before { content: "\f17b" } .fa-bots:before { content: "\e340" } .fa-free-code-camp:before { content: "\f2c5" } .fa-hornbill:before { content: "\f592" } .fa-js:before { content: "\f3b8" } .fa-ideal:before { content: "\e013" } .fa-git:before { content: "\f1d3" } .fa-dev:before { content: "\f6cc" } .fa-sketch:before { content: "\f7c6" } .fa-yandex-international:before { content: "\f414" } .fa-cc-amex:before { content: "\f1f3" } .fa-uber:before { content: "\f402" } .fa-github:before { content: "\f09b" } .fa-php:before { content: "\f457" } .fa-alipay:before { content: "\f642" } .fa-youtube:before { content: "\f167" } .fa-skyatlas:before { content: "\f216" } .fa-firefox-browser:before { content: "\e007" } .fa-replyd:before { content: "\f3e6" } .fa-suse:before { content: "\f7d6" } .fa-jenkins:before { content: "\f3b6" } .fa-twitter:before { content: "\f099" } .fa-rockrms:before { content: "\f3e9" } .fa-pinterest:before { content: "\f0d2" } .fa-buffer:before { content: "\f837" } .fa-npm:before { content: "\f3d4" } .fa-yammer:before { content: "\f840" } .fa-btc:before { content: "\f15a" } .fa-dribbble:before { content: "\f17d" } .fa-stumbleupon-circle:before { content: "\f1a3" } .fa-internet-explorer:before { content: "\f26b" } .fa-telegram-plane:before, .fa-telegram:before { content: "\f2c6" } .fa-old-republic:before { content: "\f510" } .fa-square-whatsapp:before, .fa-whatsapp-square:before { content: "\f40c" } .fa-node-js:before { content: "\f3d3" } .fa-edge-legacy:before { content: "\e078" } .fa-slack-hash:before, .fa-slack:before { content: "\f198" } .fa-medrt:before { content: "\f3c8" } .fa-usb:before { content: "\f287" } .fa-tumblr:before { content: "\f173" } .fa-vaadin:before { content: "\f408" } .fa-quora:before { content: "\f2c4" } .fa-reacteurope:before { content: "\f75d" } .fa-medium-m:before, .fa-medium:before { content: "\f23a" } .fa-amilia:before { content: "\f36d" } .fa-mixcloud:before { content: "\f289" } .fa-flipboard:before { content: "\f44d" } .fa-viacoin:before { content: "\f237" } .fa-critical-role:before { content: "\f6c9" } .fa-sitrox:before { content: "\e44a" } .fa-discourse:before { content: "\f393" } .fa-joomla:before { content: "\f1aa" } .fa-mastodon:before { content: "\f4f6" } .fa-airbnb:before { content: "\f834" } .fa-wolf-pack-battalion:before { content: "\f514" } .fa-buy-n-large:before { content: "\f8a6" } .fa-gulp:before { content: "\f3ae" } .fa-creative-commons-sampling-plus:before { content: "\f4f1" } .fa-strava:before { content: "\f428" } .fa-ember:before { content: "\f423" } .fa-canadian-maple-leaf:before { content: "\f785" } .fa-teamspeak:before { content: "\f4f9" } .fa-pushed:before { content: "\f3e1" } .fa-wordpress-simple:before { content: "\f411" } .fa-nutritionix:before { content: "\f3d6" } .fa-wodu:before { content: "\e088" } .fa-google-pay:before { content: "\e079" } .fa-intercom:before { content: "\f7af" } .fa-zhihu:before { content: "\f63f" } .fa-korvue:before { content: "\f42f" } .fa-pix:before { content: "\e43a" } .fa-steam-symbol:before { content: "\f3f6" } :host, :root { --fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free" } @font-face { font-family: "Font Awesome 6 Free"; font-style: normal; font-weight: 400; font-display: block; src: url(./fa-regular-400.woff2) format("woff2"), url(./fa-regular-400.ttf) format("truetype") } .fa-regular, .far { font-weight: 400 } :host, :root { --fa-style-family-classic: "Font Awesome 6 Free"; --fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free" } @font-face { font-family: "Font Awesome 6 Free"; font-style: normal; font-weight: 900; font-display: block; src: url(./fa-solid-900.woff2) format("woff2"), url(./fa-solid-900.ttf) format("truetype") } .fa-solid, .fas { font-weight: 900 } @font-face { font-family: "Font Awesome 5 Brands"; font-display: block; font-weight: 400; src: url(./fa-brands-400.woff2) format("woff2"), url(./fa-brands-400.ttf) format("truetype") } @font-face { font-family: "Font Awesome 5 Free"; font-display: block; font-weight: 900; src: url(./fa-solid-900.woff2) format("woff2"), url(./fa-solid-900.ttf) format("truetype") } @font-face { font-family: "Font Awesome 5 Free"; font-display: block; font-weight: 400; src: url(./fa-regular-400.woff2) format("woff2"), url(./fa-regular-400.ttf) format("truetype") } @font-face { font-family: "FontAwesome"; font-display: block; src: url(./fa-solid-900.woff2) format("woff2"), url(./fa-solid-900.ttf) format("truetype") } @font-face { font-family: "FontAwesome"; font-display: block; src: url(./fa-brands-400.woff2) format("woff2"), url(./fa-brands-400.ttf) format("truetype") } @font-face { font-family: "FontAwesome"; font-display: block; src: url(./fa-regular-400.woff2) format("woff2"), url(./fa-regular-400.ttf) format("truetype"); unicode-range: u+f003, u+f006, u+f014, u+f016-f017, u+f01a-f01b, u+f01d, u+f022, u+f03e, u+f044, u+f046, u+f05c-f05d, u+f06e, u+f070, u+f087-f088, u+f08a, u+f094, u+f096-f097, u+f09d, u+f0a0, u+f0a2, u+f0a4-f0a7, u+f0c5, u+f0c7, u+f0e5-f0e6, u+f0eb, u+f0f6-f0f8, u+f10c, u+f114-f115, u+f118-f11a, u+f11c-f11d, u+f133, u+f147, u+f14e, u+f150-f152, u+f185-f186, u+f18e, u+f190-f192, u+f196, u+f1c1-f1c9, u+f1d9, u+f1db, u+f1e3, u+f1ea, u+f1f7, u+f1f9, u+f20a, u+f247-f248, u+f24a, u+f24d, u+f255-f25b, u+f25d, u+f271-f274, u+f278, u+f27b, u+f28c, u+f28e, u+f29c, u+f2b5, u+f2b7, u+f2ba, u+f2bc, u+f2be, u+f2c0-f2c1, u+f2c3, u+f2d0, u+f2d2, u+f2d4, u+f2dc } @font-face { font-family: "FontAwesome"; font-display: block; src: url(./fa-v4compatibility.woff2) format("woff2"), url(./fa-v4compatibility.ttf) format("truetype"); unicode-range: u+f041, u+f047, u+f065-f066, u+f07d-f07e, u+f080, u+f08b, u+f08e, u+f090, u+f09a, u+f0ac, u+f0ae, u+f0b2, u+f0d0, u+f0d6, u+f0e4, u+f0ec, u+f10a-f10b, u+f123, u+f13e, u+f148-f149, u+f14c, u+f156, u+f15e, u+f160-f161, u+f163, u+f175-f178, u+f195, u+f1f8, u+f219, u+f27a } ================================================ FILE: src/site/css/inter/inter.css ================================================ @font-face { font-family: 'Inter'; font-style: normal; font-weight: 100; font-display: swap; src: url("woff2/Inter-Thin.woff2") format("woff2"), url("woff/Inter-Thin.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 100; font-display: swap; src: url("woff2/Inter-ThinItalic.woff2") format("woff2"), url("woff/Inter-ThinItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 200; font-display: swap; src: url("woff2/Inter-ExtraLight.woff2") format("woff2"), url("woff/Inter-ExtraLight.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 200; font-display: swap; src: url("woff2/Inter-ExtraLightItalic.woff2") format("woff2"), url("woff/Inter-ExtraLightItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 300; font-display: swap; src: url("woff2/Inter-Light.woff2") format("woff2"), url("woff/Inter-Light.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 300; font-display: swap; src: url("woff2/Inter-LightItalic.woff2") format("woff2"), url("woff/Inter-LightItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url("woff2/Inter-Regular.woff2") format("woff2"), url("woff/Inter-Regular.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 400; font-display: swap; src: url("woff2/Inter-Italic.woff2") format("woff2"), url("woff/Inter-Italic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url("woff2/Inter-Medium.woff2") format("woff2"), url("woff/Inter-Medium.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 500; font-display: swap; src: url("woff2/Inter-MediumItalic.woff2") format("woff2"), url("woff/Inter-MediumItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap; src: url("woff2/Inter-SemiBold.woff2") format("woff2"), url("woff/Inter-SemiBold.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 600; font-display: swap; src: url("woff2/Inter-SemiBoldItalic.woff2") format("woff2"), url("woff/Inter-SemiBoldItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 700; font-display: swap; src: url("woff2/Inter-Bold.woff2") format("woff2"), url("woff/Inter-Bold.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 700; font-display: swap; src: url("woff2/Inter-BoldItalic.woff2") format("woff2"), url("woff/Inter-BoldItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 800; font-display: swap; src: url("woff2/Inter-ExtraBold.woff2") format("woff2"), url("woff/Inter-ExtraBold.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 800; font-display: swap; src: url("woff2/Inter-ExtraBoldItalic.woff2") format("woff2"), url("woff/Inter-ExtraBoldItalic.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: normal; font-weight: 900; font-display: swap; src: url("woff2/Inter-Black.woff2") format("woff2"), url("woff/Inter-Black.woff") format("woff"); } @font-face { font-family: 'Inter'; font-style: italic; font-weight: 900; font-display: swap; src: url("woff2/Inter-BlackItalic.woff2") format("woff2"), url("woff/Inter-BlackItalic.woff") format("woff"); } /* ------------------------------------------------------- Variable font. Usage: html { font-family: 'Inter', sans-serif; } @supports (font-variation-settings: normal) { html { font-family: 'Inter var', sans-serif; } } */ @font-face { font-family: 'Inter var'; font-weight: 100 900; font-display: swap; font-style: normal; font-named-instance: 'Regular'; src: url("woff2/Inter-roman.var.woff2") format("woff2"); } @font-face { font-family: 'Inter var'; font-weight: 100 900; font-display: swap; font-style: italic; font-named-instance: 'Italic'; src: url("woff2/Inter-italic.var.woff2") format("woff2"); } /* -------------------------------------------------------------------------- [EXPERIMENTAL] Multi-axis, single variable font. Slant axis is not yet widely supported (as of February 2019) and thus this multi-axis single variable font is opt-in rather than the default. When using this, you will probably need to set font-variation-settings explicitly, e.g. * { font-variation-settings: "slnt" 0deg } .italic { font-variation-settings: "slnt" 10deg } */ @font-face { font-family: 'Inter var experimental'; font-weight: 100 900; font-display: swap; font-style: oblique 0deg 10deg; src: url("woff2/Inter.var.woff2") format("woff2"); } ================================================ FILE: src/site/css/styles.css ================================================ @import './inter/inter.css'; @import './fira_code/fira_code.css'; @import './fontawesome/font-awesome.css'; body { line-height: 1.4em; font: 400 16px/1.5 'Inter'; background: #ecf0f4; color: #111111; margin: 0; padding: 0; border: 0; vertical-align: baseline; } .wrapper { max-width: calc(800px - (30px * 2)); margin-left: auto; margin-right: auto; padding-left: 30px; padding-right: 30px; padding-bottom: 100px; } #navbar { height: 25px; margin-bottom: 10px; padding-top: 15px; font-size: 25px; } #navbar span { display: inline-block; line-height: normal; vertical-align: middle; float: right; } #navbar span.external { padding-left: 30px; } #navbar span.internal { padding-right: 30px; float: left !important; font-weight: bold; text-transform: uppercase; } @media only screen and (max-width: 600px) { #navbar { font-size: 17px; } #navbar span.external { padding-left: 7px; } #navbar span.internal { padding-right: 7px; font-size: 16px; } } #navbar a:hover { color: #aaa; } #navbar a { text-decoration: none; border: none; color: #111111; } a { color: #111111; text-decoration: none; border-bottom: 1px dotted #111111; } a:not([href]) { color: #aaa; } .prev_next { text-align: center; } .prev_next a { border: none; } .prev_next a[href]:hover { color: #586E75; } hr { border: none; height: 1px; color: #111111; background-color: #111111; } h1, h2 { color: #111111; margin-bottom: 12px; margin-top: 48px; padding-bottom: 0px; } h3 { color: #111111; font-size: 17px; } h1 { border-bottom: 1px solid #111111; } time { color: #a09b96; padding-bottom: 200px; text-transform: uppercase; } div.title { margin-top: 5px; font-weight: bold; } li p { padding: 0; margin: 0; } pre { border-radius: 5px; font-size: 0.85em; overflow: auto; } blockquote { margin: 0; background: #f9f9f9; border-left: 10px solid #dfdedc; color: #5f5e5c; border-top-right-radius: 5px; border-bottom-right-radius: 5px; } blockquote p { padding: 0.5em; margin: 0; } .figure { display : block; text-align: center; background: #f9f9f9; padding : 10px; } .figure img { max-width: 100%; } .highlight pre code { white-space: pre; // forces to respect
 formatting
}

.highlight pre, pre, .highlight .hll {
    background-color: #f8f8f8;
    padding: 6px 10px;
    border: none;
}

code {
    font-family: "Fira Code";
}

p code, li code {
    font-size:     0.9em;
    color:         #586e75;
}

.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #586E75; font-style: italic; font-weight: lighter} /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #586E75; font-weight: bold } /* Keyword */
.highlight .o { color: #586E75; font-weight: bold } /* Operator */
.highlight .cm { color: #586E75; font-style: italic; font-weight: lighter } /* Comment.Multiline */
.highlight .cp { color: #586E75; font-style: italic; font-weight: lighter } /* Comment.Preproc */
.highlight .c1 { color: #586E75; font-style: italic; font-weight: lighter } /* Comment.Single */
.highlight .cs { color: #586E75; font-style: italic; font-weight: lighter } /* Comment.Special */
.highlight .gd { color: #586E75; font-weight: bold } /* Generic.Deleted */
.highlight .ge { color: #586E75; font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #D0DCE3 } /* Generic.Heading */
.highlight .gi { color: #586E75; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #586E75; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #586E75; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #586E75; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #586E75; font-weight: bold } /* Keyword.Pseudo */
.highlight .kr { color: #586E75; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #586E75; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #D0DCE3 } /* Literal.Number */
.highlight .s { color: #586E75 } /* Literal.String */
.highlight .n { color: #586E75 }
.highlight .na { color: #586E75 } /* Name.Attribute */
.highlight .nb { color: #586E75 } /* Name.Builtin */
.highlight .nc { color: #586E75; font-weight: bold } /* Name.Class */
.highlight .no { color: #586E75 } /* Name.Constant */
.highlight .nd { color: #586E75; font-weight: bold } /* Name.Decorator */
.highlight .ni { color: #586E75 } /* Name.Entity */
.highlight .ne { color: #586E75; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #586E75; font-weight: bold } /* Name.Function */
.highlight .nl { color: #586E75; font-weight: bold } /* Name.Label */
.highlight .nn { color: #555555 } /* Name.Namespace */
.highlight .nt { color: #586E75 } /* Name.Tag */
.highlight .nv { color: #586E75 } /* Name.Variable */
.highlight .ow { color: #586E75; font-weight: bold } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #586E75 } /* Literal.Number.Float */
.highlight .mh { color: #586E75 } /* Literal.Number.Hex */
.highlight .mi { color: #586E75 } /* Literal.Number.Integer */
.highlight .mo { color: #586E75 } /* Literal.Number.Oct */
.highlight .sb { color: #586E75 } /* Literal.String.Backtick */
.highlight .sc { color: #586E75 } /* Literal.String.Char */
.highlight .sd { color: #586E75 } /* Literal.String.Doc */
.highlight .s2 { color: #586E75 } /* Literal.String.Double */
.highlight .se { color: #586E75 } /* Literal.String.Escape */
.highlight .sh { color: #586E75 } /* Literal.String.Heredoc */
.highlight .si { color: #586E75 } /* Literal.String.Interpol */
.highlight .sx { color: #586E75 } /* Literal.String.Other */
.highlight .sr { color: #586E75 } /* Literal.String.Regex */
.highlight .s1 { color: #586E75 } /* Literal.String.Single */
.highlight .ss { color: #586E75 } /* Literal.String.Symbol */
.highlight .bp { color: #586E75 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #586E75 } /* Name.Variable.Class */
.highlight .vg { color: #586E75 } /* Name.Variable.Global */
.highlight .vi { color: #586E75 } /* Name.Variable.Instance */
.highlight .il { color: #586E75 } /* Literal.Number.Integer.Long */

table {
    border-collapse:  collapse;
    border-spacing:   0;
    border:           1px solid #111111;
    width:            100%;
}

thead th {
    background: #111111;
    color:      #ecf0f4;
}

tr td {
    padding: 5px 10px 5px 40px;
    text-align: center;
}

tr td:first-child {
    padding-left: 10px;
}

td ul {
    display: inline;
    margin:  0;
    padding: 0;
}

td ul li {
    display:    inline;
    list-style: none;
    margin:     0;
    padding:    0;
}

td ul li:after {
    content: ", ";
}

td ul li:last-child:after {
    content: "";
}


================================================
FILE: src/site/fonts/fira/LICENSE
================================================
Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A.
with Reserved Font Name < Fira >, 

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.


================================================
FILE: src/site/fonts/fira/fira.css
================================================
@font-face{
    font-family: 'Fira Mono';
    src: url('eot/FiraMono-Regular.eot');
    src: local('Fira Mono'),
         url('eot/FiraMono-Regular.eot') format('embedded-opentype'),
         url('woff/FiraMono-Regular.woff') format('woff'),
         url('ttf/FiraMono-Regular.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
}

@font-face{
    font-family: 'Fira Mono';
    src: url('eot/FiraMono-Bold.eot');
    src: local('Fira Mono Bold'),
         url('eot/FiraMono-Bold.eot') format('embedded-opentype'),
         url('woff/FiraMono-Bold.woff') format('woff'),
         url('ttf/FiraMono-Bold.ttf') format('truetype');
    font-weight: 600;
    font-style: normal;
}


================================================
FILE: src/site/fonts/fontawesome/font-awesome.css
================================================
/*!
 *  Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
 *  License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
 */@font-face{font-family:'FontAwesome';src:url('./fontawesome-webfont.eot?v=4.4.0');src:url('./fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'),url('./fontawesome-webfont.woff2?v=4.4.0') format('woff2'),url('./fontawesome-webfont.woff?v=4.4.0') format('woff'),url('./fontawesome-webfont.ttf?v=4.4.0') format('truetype'),url('./fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}