1. sbtコーディングガイドライン

sbtコーディングガイドライン 

このページでは、sbt 1.0のコーディングスタイルとその他のガイドラインについて説明します。

全体的な目標 

sbt 1.0は主にScala 2.12をターゲットとします。Scala 2.10とのクロスビルドも行います。

古い非推奨のクリーンアップ 

1.0リリース前に、非推奨事項をクリーンアップする必要があります。

警告をゼロにする(非推奨を除く) 

Scala 2.12では、警告をゼロにすることを目指すべきです。クロスビルドに必要な場合、非推奨は例外となる可能性があります。

ドキュメント 

トレイト/クラスの実装を詳細に説明する前に、Scaladocから始めることは、その存在の必要性を考慮する必要があるため、しばしば役立ちます。

新しく導入されたすべての**公開**トレイトとクラス、そして程度は低いですが、関数とメソッドには、Scaladocが必要です。既存のsbtコードの多くはドキュメントが不足しており、時間をかけてこの状況を改善する必要があります。ドキュメントを追加したり、既存のドキュメントを改善する機会があれば、それも役立ちます。

パッケージレベルのドキュメントは、さまざまなコンポーネントがどのように相互作用するかを説明するのに最適な場所であるため、可能な限り追加/拡張することを検討してください。

優れたScaladocスタイルの詳細については、Scaladocスタイルガイドを参照してください。

モジュール設計 

小さく始める 

ビルドユーザーに公開できるメソッドが少ないほど、sbtのメンテナンスが容易になります。

公開APIは「インターフェース」に対してコーディングする必要があります 

インターフェースに対してコードを作成します。

実装の詳細を隠す 

実装の詳細は、`sbt.internal.x`パッケージに隠す必要があります。ここで`x`はメインパッケージの名前(`io`など)です。

相互依存性の削減 

依存ライブラリが少ない独立したモジュールは、再利用が容易です。

外部クラスを隠す 

標準的なScalaとJavaのクラスを除き、APIで外部クラスを公開することは避けてください。

内部モジュールを隠す 

公開用途がないモジュールは、内部モジュールとして宣言できます。

コンパイラフラグ 

-encoding utf8
-deprecation
-feature
-unchecked
-Xlint
-language:higherKinds
-language:implicitConversions
-Xfuture
-Yinline-warnings
-Yno-adapted-args
-Ywarn-dead-code
-Ywarn-numeric-widen
-Ywarn-value-discard
-Xfatal-warnings

回避できない警告がある場合は、`-Xfatal-warnings`を削除できます。

パッケージ名と組織名 

IOレイヤーの場合は`sbt.io`など、パッケージ名にレイヤー名を付加して使用します。公開アーティファクトの組織名は`org.scala-sbt`のままにしてください。

バイナリ耐性 

バイナリ耐性に関する良い概要は、Joshの2012年の講演です。ここのガイドラインは、主に公開されているAPIに適用されます。

MiMa 

MiMaを使用してください。

公開トレイトは`def`宣言のみを含む必要があります 

  • トレイト内の`val`または`var`は、サブクラスで生成されるコードと人工的な`Foo$class.$init$`になります。
  • `lazy val`は、サブクラスで生成されるコードになります。

抽象クラスも役立ちます 

To trait, or not to trait? 抽象クラスはトレイトほど柔軟ではありませんが、トレイトはバイナリ互換性に関してより多くの問題を引き起こします。抽象クラスは、Javaとの相互運用性も向上しています。

トレイトと抽象クラスをシールする 

クラスを開いたままにする必要がない場合は、シールします。

リーフクラスをファイナライズする 

クラスを開いたままにする必要がない場合は、ファイナライズします。

タイプリストとサブクラスの継承 

純粋なトレイトを使ったタイプリストパターンは、サブクラス化よりもバイナリ互換性の維持を容易にする可能性があります。

ケースクラスを避け、sbt-datatypeを使用する 

ケースクラスはコード生成に関与するため、時間の経過とともにバイナリ互換性を維持することが困難になります。

デフォルトパラメータ値よりもメソッドオーバーロードを優先する 

デフォルトのパラメータ値は事実上コード生成であり、維持が困難になります。

その他の公開APIに関する事項 

sbt公開APIに関するその他のガイドラインを以下に示します。

Stringly-typedプログラミングを避ける 

データ型を定義します。

`def apply`の過剰使用を避ける 

`def apply`は、型`T`を返すコンパニオンオブジェクトのファクトリメソッドに予約する必要があります。

`Seq`ではなく、具体的なデータ型(`Vector`、`List`、または`Array`)を使用する 

`scala.Seq`は`scala.collection.Seq`であり、不変ではありません。`Vector`をデフォルトとして使用します。定数プリペンドが必要な場合は`List`を使用します。Javaとの相互運用性が必要な場合は`Array`を使用します。可変コレクションをインプリメンテーション内で使用することは問題ありません。

`Set`に対して副作用のある`toSeq`または何かを呼び出すことを避ける 

`contains`や`subsetOf`のような集合演算に固執する場合は、`Set`で問題ありません。多くの場合、`toSeq`が明示的または暗黙的に呼び出されたり、`map`から副作用のあるメソッドが呼び出されたりします。これにより、コードに非決定性が導入されます。

`Map`に対して`toSeq`を呼び出すことを避ける 

上記と同じです。これにより非決定性が導入されます。

Javaとの相互運用性が必要な場合、シグネチャに関数とタプルを使用しない 

関数とタプルではなく、それらをトレイトに変換します。これは、インクリメンタルコンパイルの実装など、相互運用性が懸念される場合に適用されます。

スタイルに関する事項 

scalafmtを使用する 

sbt-houserulesには、ソースコードを一貫してフォーマットするためのscalafmtが含まれています。

プロシージャ構文を避ける 

明示的な`Unit`戻り値を宣言します。

可能な場合は、コンパニオンオブジェクトにタイプリストのインスタンスを定義する 

このスタイルが推奨されます。

final class FooID {}
object FooID {
  implicit val fooIdPicklerUnpicker: PicklerUnpickler[FooID] = ???
}

構文のための暗黙の変換(enrich-my-libraryパターン)はインポートする必要があります 

コンパニオンオブジェクトとパッケージオブジェクトに暗黙のコンバーターを定義することは避けてください。

IOモジュールがRichURIというURLのエンリッチメントを導入し、LibraryManagementがModuleID構文用のGroupIDというStringのエンリッチメントを導入すると仮定します。これらの暗黙の変換は、それぞれのパッケージ内のsyntaxという名前のオブジェクトで定義する必要があります。

package sbt.io

object syntax {
  implicit def uriToRichURI(uri: URI): RichURI = new RichURI(uri)
}

すべてのレイヤーが利用可能な場合、sbtパッケージも、すべてのレイヤーからの暗黙の変換を転送するsyntaxという名前のオブジェクトを定義する必要があります。

package sbt

object syntax {
  implicit def uriToRichURI(uri: URI): io.RichURI = io.syntax.uriToRichURI(uri)
  ....
}