このドキュメントは、Scala の散文にはすでに精通しているものの、すべての ` について困惑している初心者の Scala3 開発者を対象としています。implicits
` とコード内のパラメーター化された特性。
この文書では、その理由、方法、場所、時期について説明します。 型クラス (TC).
このドキュメントを読んだ後、初心者の Scala3 開発者は、使用するための確かな知識を獲得し、ScalaXNUMX のソース コードを詳しく学ぶことができます。 たくさん Scala ライブラリを学習し、慣用的な Scala コードの作成を開始します。
その理由から始めましょう…
表現の問題
1998年には、 フィリップ・ワドラー氏はこう述べた 「表現の問題は古い問題の新しい名前である」ということです。それはソフトウェアの拡張性の問題です。 Wadler 氏の著作によると、式の問題の解決策は次の規則に従わなければなりません。
- ルール 1: の実装を許可する 既存の行動 (Scala の特性を思い浮かべてください) に適用される 新しい表現 (ケースクラスを考えてください)
- ルール 2: の実装を許可する 新しい行動 適用される 既存の表現
- ルール 3: 危険を与えてはなりません タイプセーフ
- ルール 4: 再コンパイルが必要であってはなりません 既存のコード
この問題を解決することが、この記事の糸口になります。
ルール 1: 新しい表現に対する既存の動作の実装
どのオブジェクト指向言語にも、ルール 1 の解決策が組み込まれています。 サブタイプ多型。任意の ` を安全に実装できますtrait
` への依存関係で定義されていますclass
依存関係を再コンパイルせずに、独自のコード内で ` を使用します。実際にそれを見てみましょう:
def todo = 42
type Height = Int
type Block = Int
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
object Lib2:
import Lib1.*
case class Polkadot() extends Blockchain:
override def getBlock(height: Height): Block = todo
val eth = Lib1.Ethereum()
val btc = Lib1.Bitcoin()
val dot = Lib2.Polkadot()
この架空の例では、ライブラリ `Lib1
` (5行目) 特性を定義します `Blockchain
` (6 行目) とその 2 つの実装 (9 行目と 12 行目)。 `Lib1
` はこの文書全体で同じままになります (規則 4 の施行)。
`Lib2
` (15行目) 既存の動作を実装します `Blockchain
` 新しいクラスについて `Polkadot
` (ルール 1) をタイプセーフ (ルール 3) の方法で、再コンパイルせずに `Lib1
` (ルール 4)。
ルール 2: 既存の表現に適用される新しい動作の実装
`で想像してみましょうLib2
` 新しい動作が必要です `lastBlock
` それぞれに特別に実装されます `Blockchain
`.
最初に思い浮かぶのは、パラメータのタイプに基づいて大きなスイッチを作成することです。
def todo = 42
type Height = Int
type Block = Int
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
object Lib2:
import Lib1.*
case class Polkadot() extends Blockchain:
override def getBlock(height: Height): Block = todo
def lastBlock(blockchain: Blockchain): Block = blockchain match
case _:Ethereum => todo
case _:Bitcoin => todo
case _:Polkadot => todo
object Lib3:
import Lib1.*
case class Polygon() extends Blockchain:
override def getBlock(height: Height): Block = todo
import Lib1.*, Lib2.*, Lib3.*
println(lastBlock(Bitcoin()))
println(lastBlock(Ethereum()))
println(lastBlock(Polkadot()))
println(lastBlock(Polygon()))
このソリューションは、すでに組み込まれている言語である型ベースのポリモーフィズムを弱い再実装したものです。
`Lib1
` は変更されません (この文書全体でルール 4 が適用されていることを思い出してください)。
`で実装されたソリューションLib2
`は まあまあ `でさらに別のブロックチェーンが導入されるまでLib3
`。このコードは 3 行目で実行時に失敗するため、タイプ セーフティ ルール (ルール 37) に違反します。Lib2
` はルール 4 に違反します。
別の解決策は ` を使用することですextension
`.
def todo = 42
type Height = Int
type Block = Int
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
object Lib2:
import Lib1.*
case class Polkadot() extends Blockchain:
override def getBlock(height: Height): Block = todo
def lastBlock(): Block = todo
extension (eth: Ethereum) def lastBlock(): Block = todo
extension (btc: Bitcoin) def lastBlock(): Block = todo
import Lib1.*, Lib2.*
println(Bitcoin().lastBlock())
println(Ethereum().lastBlock())
println(Polkadot().lastBlock())
def polymorphic(blockchain: Blockchain) =
// blockchain.lastBlock()
???
`Lib1
` は変更されません (文書全体でルール 4 が適用されます)。
`Lib2
` はその型の動作を定義し (21 行目)、既存の型の ` 拡張機能を定義します (23 行目と 25 行目)。
28 ~ 30 行目では、新しい動作を各クラスで使用できます。
しかし、この新しい動作を多態的に呼び出す方法はありません (行 32)。これを試みると、コンパイル エラー (行 33) が発生するか、タイプ ベースのスイッチが発生します。
このルール 2 は注意が必要です。私たちはポリモーフィズムの独自の定義と「拡張」トリックを使用してそれを実装しようとしました。そしてそれは奇妙でした。
という欠落部分があります アドホック ポリモーフィズム: 動作と型が定義されている場合は常に、型に従って動作実装を安全にディスパッチする機能。を入力 型クラス パターン。
タイプクラスパターン
タイプ クラス (略して TC) パターン レシピには 3 つのステップがあります。
- 新しい動作を定義する
- 動作を実装する
- 動作を利用する
次のセクションでは、最も簡単な方法で TC パターンを実装します。それは冗長で、扱いにくく、非実用的です。ただし、これらの警告はドキュメント内で段階的に修正される予定ですので、お待ちください。
1. 新しい動作を定義する
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
`Lib1
` はまたもや手付かずのままです。
新しい動作 is 特性によって実現されたTC。トレイトで定義された関数は、その動作のいくつかの側面を適用する方法です。
パラメータ `A
` は動作を適用するタイプを表します。これは ` のサブタイプですBlockchain
` 私たちの場合は。
いくつかの意見:
- 必要に応じて、パラメータ化された型 `
A
` は、Scala 型システムによってさらに制約される可能性があります。たとえば、「」を強制することができます。A
` になることBlockchain
`. - また、TC ではさらに多くの関数を宣言することができます。
- 最後に、各関数にはさらに多くの任意のパラメーターを含めることができます。
ただし、読みやすくするために、物事を単純にしておきます。
2. 動作を実装する
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
val ethereumLastBlock = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
val bitcoinLastBlock = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
タイプごとに新しい `LastBlock
` 動作が予期されているため、その動作の特定のインスタンスが存在します。
`Ethereum
` 実装行 22 は ` から計算されますeth
` インスタンスはパラメータとして渡されます。
`の実装LastBlock
`のための`Bitcoin
` 25行目はアンマネージIOを使用して実装されており、そのパラメータは使用されていません。
それで、「Lib2
` 新しい動作を実装します `LastBlock
`のための`Lib1
` クラス。
3. 動作を利用する
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
val ethereumLastBlock = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
val bitcoinLastBlock = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
import Lib1.*, Lib2.*
def useLastBlock[A](instance: A, behavior: LastBlock[A]) =
behavior.lastBlock(instance)
println(useLastBlock(Ethereum(lastBlock = 2), ethereumLastBlock))
println(useLastBlock(Bitcoin(), bitcoinLastBlock))
30行目 `useLastBlock
` のインスタンスを使用しますA
` と `LastBlock
` そのインスタンスに定義された動作。
33行目 `useLastBlock
` は ` のインスタンスで呼び出されますEthereum
` と ` の実装LastBlock
` で定義Lib2
`。 ` の代替実装を渡すことができることに注意してください。LastBlock[A]
` (考えてください 依存性注入).
`useLastBlock
` は、表現 (実際の A) とその動作の間の接着剤です。データと動作は分離されており、これが関数型プログラミングが提唱していることです。
議論
式の問題のルールを要約しましょう。
- ルール 1: の実装を許可する 既存の行動 適用される 新しいクラス
- ルール 2: の実装を許可する 新しい行動 適用される 既存のクラス
- ルール 3: 危険を与えてはなりません タイプセーフ
- ルール 4: 再コンパイルが必要であってはなりません 既存のコード
ルール 1 は、サブタイプ多態性を使用してすぐに解決できます。
先ほど提示した TC パターン (前のスクリーンショットを参照) はルール 2 を解決します。これはタイプ セーフ (ルール 3) であり、` には触れませんでした。Lib1
` (ルール 4)。
ただし、次のような理由により、使用するのは非現実的です。
- 33 ~ 34 行目では、そのインスタンスに沿って動作を明示的に渡す必要があります。これは余分なオーバーヘッドです。 ` と書けばいいだけです
useLastBlock(Bitcoin())
`. - 31 行目の構文は一般的ではありません。私たちはむしろ、簡潔でよりオブジェクト指向の文章を書くことを好みます。
instance.lastBlock()
` ステートメント。
TC を実際に使用するための Scala の機能をいくつか紹介しましょう。
開発者エクスペリエンスの強化
Scala には、開発者にとって TC を本当に楽しいエクスペリエンスにする独自の機能セットと糖衣構文があります。
暗黙的に
暗黙的スコープは、特定の型のインスタンスが XNUMX つだけ存在できる、コンパイル時に解決される特別なスコープです。
プログラムは、` を使用してインスタンスを暗黙のスコープに置きます。given
` キーワード。あるいは、プログラムはキーワード ` を使用して暗黙のスコープからインスタンスを取得できます。using
`.
暗黙的なスコープはコンパイル時に解決され、実行時に動的に変更する方法が知られています。プログラムがコンパイルされると、暗黙的なスコープが解決されます。実行時に、使用される暗黙的なインスタンスが欠落することはあり得ません。唯一考えられる混乱は、間違った暗黙的インスタンスを使用することによって発生する可能性がありますが、この問題は椅子とキーボードの間の生き物に残されます。
次の理由から、グローバル スコープとは異なります。
- 状況に応じて解決されます。プログラムの XNUMX つの場所では、暗黙的スコープ内で同じ特定の型のインスタンスを使用できますが、これら XNUMX つのインスタンスは異なる場合があります。
- コードは、暗黙的な使用法に達するまで、舞台裏で暗黙的な引数関数を関数に渡します。グローバルメモリ空間を使用していません。
型クラスに戻りましょう!まったく同じ例を見てみましょう。
def todo = 42
type Height = Int
type Block = Int
def http(uri: String): Block = todo
object Lib1:
trait Blockchain:
def getBlock(height: Height): Block
case class Ethereum() extends Blockchain:
override def getBlock(height: Height) = todo
case class Bitcoin() extends Blockchain:
override def getBlock(height: Height) = todo
`Lib1
` は、以前に定義したものと同じ未変更のコードです。
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
given ethereumLastBlock:LastBlock[Ethereum] = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
given bitcoinLastBlock:LastBlock[Bitcoin] = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
import Lib1.*, Lib2.*
def useLastBlock[A](instance: A)(using behavior: LastBlock[A]) =
behavior.lastBlock(instance)
println(useLastBlock(Ethereum(lastBlock = 2)))
println(useLastBlock(Bitcoin()))
19行目 新しい動作 `LastBlock
` は、前に行ったのとまったく同じように定義されています。
22 行目と 25 行目、`val
` は ` に置き換えられますgiven
`。 ` の両方の実装LastBlock
` は暗黙的なスコープに置かれます。
31行目 `useLastBlock
` 動作を宣言します `LastBlock
暗黙的なパラメータとして ` を使用します。コンパイラは ` の適切なインスタンスを解決します。LastBlock
` 呼び出し元の場所からコンテキスト化された暗黙的なスコープから取得します (行 33 と 34)。 28 行目は ` からすべてをインポートしますLib2
` (暗黙的なスコープを含む)。したがって、コンパイラは、行 22 と行 25 で定義されたインスタンスを ` の最後のパラメータとして渡します。useLastBlock
`.
ライブラリ ユーザーとしては、型クラスの使用が以前よりも簡単になりました。行 34 と行 35 では、開発者は動作のインスタンスが暗黙的なスコープに挿入されていることを確認するだけで済みます (これは単なる ` である場合もあります)。import
`)。暗黙的が ` ではない場合given
` ここでコードは `using
` それをコンパイラが彼に伝えます。
Scala は、クラス インスタンスをその動作のインスタンスとともに渡すタスクを暗黙的に容易にします。
暗黙の糖類
前のコードの 22 行目と 25 行目はさらに改善できます。 TC の実装を繰り返してみましょう。
given LastBlock[Ethereum] = new LastBlock[Ethereum]:
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] = new LastBlock[Bitcoin]:
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
22行目と25行目は、インスタンス名が未使用の場合は省略できます。
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
22 行目と 25 行目、型の繰り返しは ` に置き換えることができますwith
` キーワード。
given LastBlock[Ethereum] = _.lastBlock
given LastBlock[Bitcoin] = _ => http("http://bitcoin/last")
単一の関数を含む縮退特性を使用しているため、IDE は SAM 式を使用してコードを簡素化することを提案する場合があります。それは正しいことですが、カジュアルにコードゴルフをしている場合を除いて、これは SAM の適切な使用法ではないと思います。
Scala は、構文を合理化し、不必要な名前付け、宣言、型の冗長性を取り除くための構文糖衣を提供します。
拡張
賢く使用すると、「extension
` メカニズムにより、型クラスを使用するための構文を簡素化できます。
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
extension[A](instance: A)
def lastBlock(using tc: LastBlock[A]) = tc.lastBlock(instance)
import Lib1.*, Lib2.*
println(Ethereum(lastBlock = 2).lastBlock)
println(Bitcoin().lastBlock)
行 28 ~ 29 は汎用拡張メソッド `lastBlock
` は任意の ` に対して定義されていますA
` 付きLastBlock
` 暗黙的なスコープ内の TC パラメータ。
行 33 ~ 34 では、拡張機能はオブジェクト指向構文を活用して TC を使用します。
object Lib2:
import Lib1.*
trait LastBlock[A]:
def lastBlock(instance: A): Block
given LastBlock[Ethereum] with
def lastBlock(eth: Ethereum) = eth.lastBlock
given LastBlock[Bitcoin] with
def lastBlock(btc: Bitcoin) = http("http://bitcoin/last")
extension[A](instance: A)(using tc: LastBlock[A])
def lastBlock = tc.lastBlock(instance)
def penultimateBlock = tc.lastBlock(instance) - 1
import Lib1.*, Lib2.*
val eth = Ethereum(lastBlock = 2)
println(eth.lastBlock)
println(eth.penultimateBlock)
val btc = Bitcoin()
println(btc.lastBlock)
println(btc.penultimateBlock)
28 行目では、繰り返しを避けるために、拡張機能全体に対して TC パラメータを定義することもできます。 30 行目では、拡張子の TC を再利用して ` を定義します。penultimateBlock
` (たとえ ` で実装できたとしても)LastBlock
` 特性を直接)
TC を使用すると魔法が起こります。表現はより自然に感じられ、動作が「」であるかのような錯覚を与えます。lastBlock
` はインスタンスと混同されています。
TC付汎用タイプ
import Lib1.*, Lib2.*
def useLastBlock1[A](instance: A)(using LastBlock[A]) = instance.lastBlock
def useLastBlock2[A: LastBlock](instance: A) = instance.lastBlock
val eth = Ethereum(lastBlock = 2)
assert(useLastBlock1(eth) == useLastBlock2(eth))
行 34 では、関数は暗黙的な TC を使用します。名前が不要な場合は、TC に名前を付ける必要がないことに注意してください。
TC パターンは非常に広く使用されているため、「暗黙的な動作を持つ型」を表現するジェネリック型構文が存在します。行 36 の構文は、前の構文 (行 34) よりも簡潔です。これにより、名前のない暗黙的な TC パラメーターを具体的に宣言することが回避されます。
これで開発者エクスペリエンスのセクションは終了です。 TC を使用および定義するときに、拡張機能、暗黙的メソッド、および一部の糖衣構文がどのように乱雑でない構文を提供できるかを見てきました。
自動導出
多くの Scala ライブラリは TC を使用しており、プログラマがコード ベースに実装する必要があります。
たとえば、Circe (json 逆シリアル化ライブラリ) は TC ` を使用します。Encoder[T]
`と`Decoder[T]
` プログラマーがコードベースに実装するためのものです。実装すると、ライブラリのスコープ全体を使用できるようになります。
TC のこれらの実装は、多くの場合、 データ指向マッパー。ビジネス ロジックは必要なく、書くのは退屈で、ケース クラスとの同期を維持するのは負担です。
このような状況では、これらのライブラリは、いわゆる 自動 導出または 半自動 導出。たとえばキルケを参照 自動 および 半自動 導出。半自動導出では、プログラマはいくつかのマイナーな構文を使用して型クラスのインスタンスを宣言できますが、自動導出ではインポートを除いてコードを変更する必要がありません。
内部では、コンパイル時に汎用マクロがイントロスペクトします。 純粋なデータ構造として、ライブラリ ユーザー用の TC[T] を生成します。
一般的に TC を導出するのは非常に一般的であるため、Scala はその目的のための完全なツールボックスを導入しました。このメソッドは、導出を使用する Scala 3 の方法ではありますが、ライブラリのドキュメントで常に宣伝されているわけではありません。
object GenericLib:
trait Named[A]:
def blockchainName(instance: A): String
object Named:
import scala.deriving.*
inline final def derived[A](using inline m: Mirror.Of[A]): Named[A] =
val nameOfType: String = inline m match
case p: Mirror.ProductOf[A] => compiletime.constValue[p.MirroredLabel]
case _ => compiletime.error("Not a product")
new Named[A]:
override def blockchainName(instance: A):String = nameOfType.toLowerCase
extension[A] (instance: A)(using tc: Named[A])
def blockchainName = tc.blockchainName(instance)
import Lib1.*, GenericLib.*
case class Polkadot() derives Named
given Named[Bitcoin] = Named.derived
given Named[Ethereum] = Named.derived
println(Ethereum(lastBlock = 2).blockchainName)
println(Bitcoin().blockchainName)
println(Polkadot().blockchainName)
18行目 新しいTC `Named
` が導入されています。このTCは厳密に言えばブロックチェーンビジネスとは無関係です。その目的は、ケースクラスの名前に基づいてブロックチェーンに名前を付けることです。
まず、36 ~ 38 行目の定義に注目してください。 TC を導出するには 2 つの構文があります。
- 36 行目では、` を使用して TC インスタンスを case クラスに直接定義できます。
derives
` キーワード。コンパイラは内部で指定された ` を生成します。Named
` のインスタンスPolkadot
` コンパニオンオブジェクト。 - 37 行目と 38 行目、型クラスのインスタンスは ` を使用して既存のクラスに指定されます。
TC.derived
`
行 31 では汎用拡張が定義されており (前のセクションを参照)、`blockchainName
` は自然に使われます。
`derives
` キーワードには ` という形式のメソッドが必要ですinline def derived[T](using Mirror.Of[T]): TC[T] = ???
` これは 24 行目で定義されています。コードが何を行うかについては詳しく説明しません。大まかに言うと:
- `
inline def
` マクロを定義します - `
Mirror
` は型をイントロスペクトするためのツールボックスの一部です。ミラーにはさまざまな種類があり、コードの 26 行目では ` に焦点を当てています。Product
` ミラー (ケースクラスは製品です)。 27 行目、プログラマが ` ではないものを導出しようとした場合Product
`、コードはコンパイルされません。 - `
Mirror
` には他のタイプが含まれます。そのうちのXNUMX人、「MirrorLabel
` は型名を含む文字列です。この値は、` の実装の 29 行目で使用されます。Named
`TC。
TC 作成者は、メタ プログラミングを使用して、型を指定して TC のインスタンスを一般的に生成する関数を提供できます。プログラマーは、専用のライブラリ API または Scala 派生ツールを使用して、コードのインスタンスを作成できます。
TC を実装するために一般的なコードが必要か特定のコードが必要かにかかわらず、それぞれの状況に応じた解決策があります。
すべての利点の概要
- 表現の問題を解決します
- 新しいタイプは、従来の特性継承を通じて既存の動作を実装できます
- 新しい動作を既存の型に実装できる
- 関心事の分離
- コードは壊れておらず、簡単に削除できます。 TC はデータと動作を分離します。これは関数型プログラミングのモットーです。
- 安全です
- イントロスペクションに依存しないため、タイプセーフです。これにより、型に関係する大規模なパターン マッチングが回避されます。このようなコードを書いていることに遭遇すると、TC パターンが完全に適合するケースが見つかるかもしれません。
- 暗黙的なメカニズムはコンパイル安全です。コンパイル時にインスタンスが見つからない場合、コードはコンパイルされません。実行時に驚くことはありません。
- アドホックなポリモーフィズムをもたらす
- アドホックポリモーフィズムは通常、従来のオブジェクト指向プログラミングでは欠落しています。
- アドホックポリモーフィズムを使用すると、開発者は従来のサブタイピング (コードを結合する) を使用せずに、無関係なさまざまな型に対して同じ動作を実装できます。
- 依存関係の注入が簡単に
- TC インスタンスは、Liskov 置換原則に従って変更できます。
- コンポーネントが TC に依存している場合、モック化された TC をテスト目的で簡単に挿入できます。
カウンター表示
すべてのハンマーは、さまざまな問題に対応できるように設計されています。
型クラスは動作上の問題に対応するものであり、データの継承には使用しないでください。そのためにコンポジションを使用します。
通常のサブタイピングはより簡単です。コードベースを所有しており、拡張性を目指していない場合、型クラスは過剰になる可能性があります。
たとえば、Scala コアには ` があります。Numeric
` 型クラス:
trait Numeric[T] extends Ordering[T] {
def plus(x: T, y: T): T
def minus(x: T, y: T): T
def times(x: T, y: T): T
このような型クラスを使用することは、Scala に埋め込まれた型 (Int、BigInt など) だけでなく、ユーザー定義型 (`ComplexNumber
` たとえば)。
一方、Scala コレクションの実装では、ほとんどの場合、型クラスの代わりにサブタイプが使用されます。この設計はいくつかの理由から理にかなっています。
- コレクション API は完全で安定している必要があります。実装によって継承された特性を通じて一般的な動作を公開します。拡張性が高いことは、ここでは特別な目標ではありません。
- 使い方は簡単でなければなりません。 TC は、エンド ユーザー プログラマに精神的なオーバーヘッドを追加します。
- TC では、パフォーマンスにわずかなオーバーヘッドが発生する可能性もあります。これはコレクション API にとって重要な場合があります。
- ただし、コレクション API は、サードパーティのライブラリによって定義された新しい TC を通じて拡張可能です。
まとめ
TC は大きな問題を解決する単純なパターンであることがわかりました。 Scala の豊富な構文のおかげで、TC パターンをさまざまな方法で実装して使用できます。 TC パターンは関数型プログラミングのパラダイムに沿っており、クリーンなアーキテクチャのための素晴らしいツールです。特効薬はなく、適合する場合は TC パターンを適用する必要があります。
このドキュメントを読んで知識を得ていただければ幸いです。
コードは次の場所で入手できます https://github.com/jprudent/type-class-article。ご質問やご意見がございましたら、お気軽にお問い合わせください。必要に応じて、リポジトリ内の課題やコード コメントを使用できます。
ソフトウェアエンジニア
- SEO を活用したコンテンツと PR 配信。 今日増幅されます。
- PlatoData.Network 垂直生成 Ai。 自分自身に力を与えましょう。 こちらからアクセスしてください。
- プラトアイストリーム。 Web3 インテリジェンス。 知識増幅。 こちらからアクセスしてください。
- プラトンESG。 カーボン、 クリーンテック、 エネルギー、 環境、 太陽、 廃棄物管理。 こちらからアクセスしてください。
- プラトンヘルス。 バイオテクノロジーと臨床試験のインテリジェンス。 こちらからアクセスしてください。
- 情報源: https://www.ledger.com/blog/type-classes-in-scala3-a-beginners-guide
- :持っている
- :は
- :not
- :どこ
- ][p
- 1
- 10
- 11
- 12
- 14
- 視聴者の38%が
- 19
- 1998
- 22
- 23
- 24
- 25
- 視聴者の38%が
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 視聴者の38%が
- 36
- 7
- 8
- 9
- a
- 能力
- 私たちについて
- AC
- 従った
- Action
- 実際の
- 追加
- 擁護
- 再び
- 目指す
- アルゴリズム
- すべて
- 許す
- ことができます
- 沿って
- 既に
- また
- 代替案
- しかし
- 常に
- an
- および
- 別の
- どれか
- API
- 適用された
- 申し込む
- 適切な
- 建築
- Archive
- です
- 引数
- 記事
- AS
- 側面
- At
- 試み
- 著者
- オートマチック
- 利用できます
- 避ける
- バック
- ベース
- ベース
- BE
- なぜなら
- 初心者
- 行動
- 行動
- さ
- の間に
- ビッグ
- ブロックチェーン
- 退屈な
- 両言語で
- ボックス
- もたらす
- 広い
- BTC
- 負担
- ビジネス
- 焙煎が極度に未発達や過発達のコーヒーにて、クロロゲン酸の味わいへの影響は強くなり、金属を思わせる味わいと乾いたマウスフィールを感じさせます。
- by
- コール
- 呼ばれます
- 発信者
- 缶
- 場合
- 椅子
- 変化する
- 変更
- class
- クラス
- コード
- コードベース
- コードベース
- コレクション
- コレクション
- 来ます
- comes
- 注釈
- コマンドと
- コンパニオン
- コンプリート
- 従う
- コンポーネント
- 構図
- 懸念
- 特徴
- 結論
- 混同した
- 混乱
- 含まれています
- 基本
- 正しい
- 可能性
- 作ります
- 作成
- 生き物
- 重大な
- データ
- データ構造
- 宣言してい
- 専用の
- 定義します
- 定義済みの
- 定義する
- 定義
- 定義
- 依存関係
- 深さ
- 派生する
- 設計
- 設計
- 検出
- Developer
- 開発者
- DID
- 異なります
- 直接に
- 派遣
- ダイビング
- do
- ドキュメント
- ありません
- そうではありません
- ドント
- 動的に
- 各
- 緩和する
- 容易
- 簡単に
- 簡単に
- ed
- 埋め込まれた
- end
- 強制します
- 執行
- 楽しい
- 入力します
- エラー
- ETH
- さらに
- すべてのもの
- 正確に
- 例
- 除く
- 存在する
- 既存の
- 予想される
- 期待する
- 体験
- 説明する
- 説明
- 明示的
- 表現します
- 表現
- エクステンション
- 余分な
- 失敗
- 特徴
- 感じています
- 固定の
- フォーカス
- 焦点を当てて
- フォロー中
- フォーム
- から
- function
- 機能的な
- 機能
- さらに
- 利得
- 獲得
- 生成する
- 生成
- GitHubの
- 与えられた
- 与え
- グローバル
- グローバルスコープ
- 目標
- ガイド
- ハンマー
- ハンド
- 起こります
- 持ってる
- こちら
- 特徴
- 非常に
- 彼に
- フード
- 認定条件
- HTML
- HTTP
- HTTPS
- i
- if
- 錯覚
- 絵
- 実装する
- 実装
- 実装
- 実装
- 実装する
- import
- 輸入
- 改善されました
- in
- 含めて
- 継承
- を取得する必要がある者
- 意図された
- に
- 導入
- 関与
- 問題
- 問題
- IT
- ITS
- 危険にさらす
- JSON
- ただ
- キープ
- 知っている
- 知識
- 言語
- 姓
- リード
- 残す
- 元帳
- 左
- less
- レバレッジ
- ライブラリ
- 図書館
- ような
- LINE
- ライン
- 場所
- ロジック
- たくさん
- マクロ
- 製
- 簡単に
- マジック
- 維持する
- make
- 作る
- 方法
- 多くの
- マッチング
- 五月..
- me
- メカニズム
- メモリ
- メンタル
- 単なる
- Meta
- 方法
- かもしれない
- マインド
- マイナー
- ミラー
- 行方不明
- 他には?
- 最も
- 主に
- モットー
- しなければなりません
- 名
- 名前付き
- 命名
- ナチュラル
- 必要
- 必要とされる
- 決して
- 新作
- いいえ
- 注意
- オブジェクト
- of
- 提供
- オファー
- 頻繁に
- 古い
- on
- かつて
- ONE
- の
- or
- その他
- 私たちの
- でる
- 輪郭
- が
- 自分の
- パラダイム
- パラメーター
- パラメータ
- 部
- 特定の
- パーティー
- パス
- 渡された
- パス
- 通過
- パターン
- 完璧に
- パフォーマンス
- ピース
- プラトン
- プラトンデータインテリジェンス
- プラトデータ
- お願いします
- 可能
- 実用的
- 好む
- PLM platform.
- 前
- 前に
- 原則
- 問題
- 問題
- プロダクト
- 演奏曲目
- プログラマー
- プログラマ
- プログラミング
- 適切な
- 提供します
- 目的
- 目的
- 置きます
- 置く
- 質問
- 範囲
- むしろ
- リーチ
- 達した
- リーディング
- 本当に
- 理由
- 再生タイヤ
- レシピ
- 頼る
- 残る
- 覚えています
- 除去
- 置き換え
- 倉庫
- 表現
- 表し
- 解決
- 尊重
- 再利用
- 富裕層
- ルール
- ルール
- ランタイム
- s
- 安全な
- 安全に
- 安全性
- 酒
- サム
- 同じ
- シーン
- スコープ
- セクション
- セクション
- 見て
- センス
- セッションに
- いくつかの
- ショート
- すべき
- シルバー
- 簡単な拡張で
- 簡素化する
- 単純化
- 状況
- 小さい
- So
- ソフトウェア
- 固体
- 溶液
- 解決する
- 一部
- 何か
- ソース
- ソースコード
- スペース
- 話す
- 特別
- 特定の
- 特に
- 安定した
- start
- ステートメント
- 手順
- ステップ
- まだ
- 簡単な
- 流線
- 文字列
- 構造
- そのような
- シュガー
- 示唆する
- スーツ
- 想定
- 確か
- 驚き
- スイッチ
- 同期。
- 構文
- 取る
- 仕事
- 伝える
- テスト
- より
- 感謝
- それ
- ソース
- アプリ環境に合わせて
- それら
- そこ。
- 彼ら
- もの
- 物事
- 考える
- 三番
- この
- それらの
- しかし?
- 介して
- 時間
- 〜へ
- ツール
- ツールボックス
- 豊富なツール群
- 触れ
- 伝統的な
- 試み
- 真に
- 試します
- 2
- type
- アンコモン
- 下
- ユニーク
- 名前なし
- 不要
- まで
- 手付かずの
- 未使用
- に
- 使用法
- つかいます
- 中古
- ユーザー
- users
- 使用されます
- いつもの
- 通常
- 値
- さまざまな
- 精通している
- 非常に
- 欲しいです
- ました
- 仕方..
- 方法
- we
- 弱い
- この試験は
- 何ですか
- いつ
- 一方
- which
- 誰
- 全体
- なぜ
- 広く
- 意志
- 賢い
- 無し
- でしょう
- 書きます
- 書き込み
- 間違った
- まだ
- You
- あなたの
- あなた自身
- ゼファーネット