Skip to content

Latest commit

 

History

History
216 lines (151 loc) · 5.74 KB

CONTRIBUTING.org

File metadata and controls

216 lines (151 loc) · 5.74 KB

Contributing to VectorPipe

Prerequisite Knowledge

GeoTrellis

GeoTrellis sublibraries and types are used heavily throughout vectorpipe, particularly its vector and vectortile packages.

Apache Spark

RDD usage is fairly prevalent, so knowledge of Spark internals may help you, depending on your task.

Cats

The Functional Programming library that adds certain necessities missing from vanilla Scala. This is not at all necessary for using vectorpipe, but is used here and there within its internal machinery.

OpenStreetMap

Knowledge of how OpenStreetMap data is formatted will help you immensely. Terms:

  • Element
  • Node
  • Way
  • Relation

Development Dependencies

Otherwise, all Scala dependencies (including compilers) will be automatically downloaded by sbt.

Style Guide

When contributing code changes to vectorpipe, bear in mind that we make a few stylistic choices in order to minimize code complexity:

Code and Directory Layout

  • Code mechanics relevant to the workings of the library but irrelevant to the user should be relegated to a module under vectorpipe.*.internal, where the * is whatever parent module you’re working in.
  • Type aliases live in package objects:
package vectorpipe

package object foo {
  type Bar = Int
}
  • Typeclass instances live in the companion object of the class they’re for:
import cats._

case class Foo[T](t: T)

object Foo {
  implicit val fooFunctor: Functor[Foo] = new Functor[Foo] {
    def map[A, B](fa: Foo[A])(f: A => B): Foo[B] = ???
  }
}

This is to give immediate “visibility” of instances to their corresponding types. Just by importing Foo, you have access to all its instances without having to think about them. This decreases import confusion.

Scala Features to Avoid

Method Overloading and Default Arguments

We avoid method overloading:

case class Foo[T](t: T) {
  def bar(a: Int): Bar = ???

  // avoid
  def bar(a: Int, b: Int): Bar = ???
}

We avoid default arguments:

case class Foo[T](t: T) {
  // avoid
  def bar(a: Int, b: Option[Int] = None): Bar = ???
}

Since this is method overloading in disguise.

Exceptions

We avoid throwing Exceptions:

/* Surely this function will obey its contract... */
def innocent(path: String): Foo

sbt> innocent("/wrong/file/path/or/bad/data.txt")
java.lang.YouCouldntHaveForeseenThisException

Exceptions were intentionally left out of new languages like Golang, Rust, and Elm. In Scala, we can use vanilla Try and Either, or EitherT from Cats or ScalaZ to model potential errors:

def innocent(path: String): Either[String, Foo]

/* "Mixing Contexts", i.e. the ability to run concurrently and to fail safely */
def innocentIO(path: String): EitherT[Future, String, Foo]

Non-data Classes

We avoid classes that don’t represent data:

class Fooifizer(val bestArg: Type) {
  def work(arg: Type): Unit = { ??? }
}

Instead, we call a spade a spade and write a stand-alone function:

/* Put this in an appropriate companion object, or the package object */
def fooifize(bestArg: Type, arg: Type): Unit = { ??? }

Miscellaneous

We avoid .apply returning a type other than the parent object:

object Foo {
  // avoid
  def apply(...): Bar = ...
}

// Or else you can write code like:
val x = Foo(...)  // hard to know what x's type is.

We avoid implicit conversions:

case class Foo(...)

case class Bar(...) {
  def bar: ??? = ...
}

object Foo {
  // avoid
  implicit def foo2Bar(foo: Foo): Bar = ...
}

// Or else you can write code like:
val x = Foo(...).bar // where did `bar` come from?

Typeclasses should be implemented via the implicit-val-within-companion-object pattern.

Updating the Microsite

All content files can be found in src/main/tut/. After making your desired changes, you can confirm them by running the following in sbt:

sbt> makeMicrosite

This will build the site as well as compile every Scala example. If something about the API has changed and the examples are no longer valid, these docs will fail to build. This is a good thing! Just make the appropriate extra changes and rebuild.

To view your built site locally, navigate to target/site/ and run jekyll serve. Be careful: The main content of the site will be visible at 127.0.0.1:4000/vectorpipe/. Without the vectorpipe on the end, you won’t see anything.

If you have write permission to the main VectorPipe repo on Github, then your updated microsite can be published to https://geotrellis.github.io/vectorpipe/ via:

sbt> publishMicrosite

Publishing to Bintray

Provided you have permissions to publish to Azavea’s Bintray, all that’s necessary to proceed is:

sbt> publish

in your SBT shell.