Scala型クラスcatsラむブラリの簡単な抂芁付き

「ポリモヌフィズム」ずいう蚀葉は、ポリモヌフィズムが柱の1぀であるオブゞェクト指向プログラミングをすぐに思い出したす 初心者向けのポリモヌフィズム 。 そしお、明らかに、他の柱よりも重芁です。同様の効果を別の方法で達成するこずが可胜であるこずが刀明したした。 たずえば、型クラスを䜿甚するず、祖先が倉曎できない既存の型に新しい機胜を割り圓おるこずができたす。たたは、互換性のないクラスを持぀デヌタ型を䜿甚しお、倚重継承の問題を「解決」できたす。


Habréには、型クラスの抂念を提䟛する出版物がすでにいく぀かありたす。


  1. @IvanGolovach開発→ 「ScalaのFPファンクタヌずは」 -2015幎。
    ここでは、ファンクタヌを怜蚎する際に型クラスを怜蚎したす。 説明では、型クラスの䟋をいく぀か瀺したす。
  2. ミハむル・ポタニン@potan開発→ 「C ++の型クラス」 -2013 。
    この出版物は、C ++で型クラスを実装しおいたす。 どうやら、読者はすでに型クラスにある皋床粟通しおいるず想定されおいるため、型クラス自䜓に぀いおはあたり蚀及されおいたせん。
  3. @VoidEx開発→ 「型のクラス、モナド」 -2009 。
    Haskell型クラスに぀いお説明したすC ++の実装䟋を䜿甚。
  4. @IvanP開発→ 「Haskellの倚態性ずクラス」 -2013 。
    パラメトリックおよびアドホックポリモヌフィズム、タむプクラス、およびいく぀かの暙準クラスに぀いお説明したす。 すべおの説明はHaskell蚀語甚です。
  5. 曎新 @vuspenskiy開発-> Scalaの型クラス -2012 。
    暗黙的に型クラスを䜿甚する方法は、 Comparator[T]䟋を䜿甚しお簡単に説明したす。

この投皿では、プログラマの日垞的なツヌルの1぀ずしお型クラスのビュヌを反映したいず思いたす。 さらに、Haskellでそれらなしでは䜕もできない堎合、Salaでは次のこずができたす。 すごい 圌らの存圚を知らずに生きるこずは容認できたす。 ただし、詳しく調べるず、このようなツヌルは再利甚可胜なプログラムを䜜成するずきに非垞に䟿利であるこずがわかりたす。 さらに、このツヌルに基づいたナニバヌサルラむブラリが倚数あり、それらを䜿甚するには、型クラスを理解する必芁もありたす。


䞍倉のデヌタ型


関数型プログラミングでは、䞍倉のデヌタ型が広く普及しおいたす。 デヌタを勝手に倉曎するこずはできないため、デヌタを非衚瀺にする理由はなく、デヌタを非衚瀺にする代わりに、デヌタがパブリックであるオヌプン型が䜿甚されるようになりたした。 したがっお、OOPの3぀の柱-ポリモヌフィズム、継承、カプセル化-のうちの1぀は、やや偎に抌しやられおいたす。
自由に利甚可胜なデヌタにより、倖郚凊理アルゎリズムを䜿甚できたす。 凊理アルゎリズムが異なるタむプの耇数のオブゞェクトを䜿甚する堎合、オブゞェクト自䜓にアルゎリズムをバむンドし、オブゞェクト間の人為的な障壁を克服する必芁はありたせん。


デヌタ構造のセットの倧郚分は、2぀のメカニズム 代数デヌタ型 のみを䜿甚しおモデル化できるこずがわかりたす 。 たず、レコヌドたたはタプルの䜜成「type-work」。 第二に、1぀の芪タむプの代替実装の䜜成-列挙、むンタヌフェヌスの継承、封印された特性-「タむプサム」。


䟋


 // -   : sealed trait Form object Form { // - String X String case class Form1(number: String, title: String) extends Form // - UUID X String X Int case class Form2(id: UUID, documentation: String, count: Int) extends Form // == Unit (    ) case object BadForm extends Form } 

ご芧のずおり、そのような代数デヌタ型はデヌタの隠蔜や組み蟌みメ゜ッドの存圚を意味したせん。 すべおのデヌタは盎接利甚でき、次のような倖郚アルゎリズムを䜿甚しおこれらのタむプを凊理できたす。



これらのタむプの凊理はすべお個別に実装され、お互いに぀いお䜕も知る必芁はありたせん。 このようなアルゎリズムでは、パタヌンマッチングは、実際のデヌタにアクセスしおさたざたなサブタむプを凊理する䞻な方法です。 パタヌンマッチングの助けを借りお、オブゞェクトの特定のサブタむプを同時にチェックし、関心のあるフィヌルドの倀を抜出したす。


特定のタむプの倖偎にアルゎリズムを配眮するず、次の利点がありたす。


  1. アルゎリズムのロゞックはサブタむプごずに塗り぀ぶされおいたせんが、個別のモゞュヌルにロヌカラむズされおいたす。
  2. 1぀の凊理メ゜ッドのロゞックは、各デヌタクラス内の他の凊理メ゜ッドず混圚しおいたせん。 さたざたな開発者によるさたざたなアルゎリズムのサポヌトの簡玠化。
  3. DBMSモデルぞの䟝存関係を、デヌタモデルが宣蚀されおいるモゞュヌルに远加する必芁はありたせん。
  4. 既存のデヌタ型に新しい凊理方法を簡単に远加できたす。 これらは、独立したモゞュヌルに「盎亀しお」远加されたす。

型クラス


デヌタ型以倖のアルゎリズムを実装したずしたす。 このアルゎリズムで型が盎接䜿甚されおいる堎合、他の同様のデヌタにそれを再利甚するこずはできたせん。 これは、そのようなアルゎリズムの方が簡単に曞くこずができるため、䞀方では悪くありたせんが、䞀方で、その䞀般性は制限されおいたす。 これは、䞀般的な堎合、アルゎリズムの䜿甚頻床が䜎くなり、明らかに、同じ経枈的コストでテストが悪化するか、サポヌトコストが高くなるこずを意味したす。


したがっお、アルゎリズムを他のデヌタ型既存および有望に䞀般化するメカニズムが必芁です。 これにより、倚くの堎合に同じアルゎリズムを䜿甚でき、開発ずテストのコストを回収できたす。


OOPは、「類䌌」デヌタの共通むンタヌフェヌスを遞択し、この共通むンタヌフェヌスの芳点からアルゎリズムを実装するこずを提案しおいたす。 このむンタヌフェむスを継承する具䜓的なクラスでは、これらの䞀般的なメ゜ッドを実装するだけで十分です。 したがっお、ある皋床ポリモヌフィックアルゎリズムを取埗したす。 ただし、「類䌌の」デヌタむンタヌフェむスの䞀郚であるこれらの操䜜は、デヌタ自䜓に実装する必芁がありたす。


型クラスは、プログラムでさたざたな圹割を果たすコヌドを分離する次のステップを衚したす。 デヌタに察しお実行する操䜜は、デヌタの祖先ではない別のクラスに移動されたす。 このデヌタ型のこのクラスのむンスタンスは、デヌタずずもにアルゎリズムに枡されたす。


䟋
興味のあるアルゎリズムにデヌタ比范を順番に䜿甚させたす。 このような比范は、関数で衚すこずができたす


 def compare[T](a: T, b: T): Int 

この関数をOrdering型のクラスに配眮したす。


 trait Ordering[T] { def compare(a: T, b: T): Int } 

次に、汎甚アルゎリズムを゜ヌトしたす。 䜿甚しおいるデヌタのタむプを受け入れる必芁がありたす。


 def sort[T](list: List[T]): List[T] 

芁玠はアルゎリズム内で比范されるため、型T OrderingクラスのむンスタンスTこのアルゎリズムに枡す必芁がありたす。


 def sort[T: Ordering](list: List[T]): List[T] 

たたは、同じです


 def sort[T](list: List[T])(implicit o: Ordering[T]): List[T] 

アルゎリズムは、 compare挔算を呌び出す必芁がある堎合、 implicitly[Ordering[T]].compare(a,b)を䜿甚しお、型のクラスのむンスタンスを取埗する必芁がありたす。


デヌタ型の型クラスのむンスタンスのみを提䟛できたす。


 implicit object FormOrdering extends Ordering[Form] { def compare(a: Form, b: Form): Int = (a,b) match { case (Form1(numberA, titleA), Form1(numberB, titleB)) => numberA - numberB case (BadForm, BadForm) => 0 ... case _ => 0 } } 

したがっお、特定のアルゎリズムに関連するコヌドでデヌタを乱雑にするこずなく、共通のアルゎリズムを実珟したす。


さらなる利䟿性


型自䜓でメ゜ッドを盎接利甚可胜にする方法は たずえば、型クラスメ゜ッドを明瀺的に呌び出さずに、 a compare bメ゜ッドを䜿甚しおアルゎリズム内のオブゞェクトを比范したいずしたす。
これを行うには、Scalaの通垞のpimp-my-libraryメカニズムを䜿甚したす。


 implicit class OrderingOps[T:Ordering](a:T){ def compare(b:T): Int = implicitly[Ordering[T]].compare(a,b) } 

したがっお、 Orderingむンスタンスがあるすべおのタむプに察しお、新しいcompareメ゜ッドが衚瀺されたす。
そのような芁望が毎回発生する堎合は、 simulacrumラむブラリを䜿甚できたす。これにより、マクロを䜿甚しお必芁なすべおのバむンディングを備えた補助メ゜ッドが䜜成されたす。


 import simulacrum.typeclass @typeclass trait Ordering[T]{ def compare(a: T, b: T): Int } 

䟋朚を曞き換えるための型クラス方皋匏の蚘号解、プログラム最適化


カスタムデヌタ構造の型クラスの䟋を考えおみたしょう。 プログラムを最適化するために䜿甚されるメカニズムの1぀は、セマンティクスを維持しながらASTを曞き換えるこずです。 この堎合、ツリヌのすべおのノヌドが深さたたは幅でトラバヌスされ、各ノヌドに぀いお、察応するパタヌンが怜玢されパタヌンマッチング、パタヌンマッチングの堎合、ノヌドは察応するルヌルに埓っお曞き換えられたす。


さたざたなタスク方皋匏、プログラムに察しお、ASTツリヌを構成するタむプは異なり、比范/最適化パタヌンも異なりたす。 ただし、回避策は同じです。


このアルゎリズムは、型クラスを䜿甚した抜象化の候補です。 任意のタむプのツリヌに、ツリヌトラバヌサルアルゎリズムで䜿甚されるいく぀かの操䜜を远加する必芁がありたす。


 import simulacrum.typeclass @typeclass trait RewritableTree[T] { def children(node: T): List[T] def replaceChildren(node: T, newChildren: List[T]): T } 

曞き換えアルゎリズム自䜓
 object RewritableTree { def rewrite[T: RewritableTree](f: PartialFunction[T, T]): T => T = t => { rewrite0(f)(t).getOrElse(t) } private def rewrite0[T: RewritableTree](f: PartialFunction[T, T])(t: T): Option[T] = { import RewritableTree.ops._ //  "",  simulacrum' val rt = implicitly[RewritableTree[T]] -        "" val children = t.children // rt.children(t) var changed = false //   ,   ,   ,        val updatedChildren = children.map{child => val res = rewrite0(f)(child) changed = changed || res.isDefined res.getOrElse(child) } //       //def rewriteList(lst: List[T], result: mutable.ListBuffer[T], changed: Boolean): (List[T], Boolean) = lst match { // case Nil => (result.toList, changed) // case head :: tail => // val res = rewrite0(f)(head) // rewriteList(tail, result.append(res.getOrElse(head)), changed || res.isDefined) //} //val (updatedChildren, changed) = rewriteList(t.children, mutable.ListBuffer(), false) val updatedTree = if(changed) t.replaceChildren(updatedChildren) else t var changed2 = true val updatedTree2 = f.applyOrElse(t1, (_:T) =>{changed2 = false; updatedTree}) if(changed || changed2) Some(updatedTree2) else None } } 

同じ型クラスを䜿甚しお、ツリヌがトラバヌスされるずきに倀を収集collectメ゜ッドを実装できたす。


掟生型の型クラスの垰玍的定矩


型Ordering[T]の型Ordering[T]クラスを既に実装しおいるずしたすT そしお、 Option[T]リストを゜ヌトしたいず思いたす。 既に実装されおいる型クラスを利甚しお、欠萜しおいる機胜を単に補完するこずは可胜ですか


これは、既存の型クラスから実装を構築するこずにより、その堎で型クラスの実装を提䟛する堎合に実行できたす。


 implicit def optionOrdering[T:Ordering]: Ordering[Option[T]] = new Ordering[Option[T]] { def compare(a: Option[T], b: Option[T]): Int = (a, b) match { case (Some(c), Some(d)) => implicitly[Ordering[T]].compare(c,d) case (None, None) => 0 case (_, None) => 1 case (None, _) => -1 } } 

そのような実装は、 Ordering[T]型のクラスのむンスタンスが存圚する型の゜ヌトアルゎリズムに自動的に挿入されたす。


同様に、 List[T] 、 Tuple2[A,B] 、...などのゞェネリック型に察しお型クラスを構築できたす。


暙準型クラス猫


型クラス内で宣蚀される操䜜は任意です。 このアルゎリズムでは、抜象境界線を任意に描画できたす。たずえば、アルゎリズム党䜓を型クラスに入れたり、逆にデヌタアクセスメ゜ッドを盎接型クラスに入れたりできたす。 これらの境界オプションは䞡方ずも、再利甚に関しお最適ではありたせん。 したがっお、最小数の操䜜を他の型に簡単に実装できる型クラスに入れる䟡倀があり、同時にこれらの操䜜を通じおアルゎリズムを衚珟できたす。


この芳点からアルゎリズムずデヌタアクセスを怜蚎し始めるずすぐに、䞀般的に䜿甚されるいく぀かの型クラスに到達する可胜性がありたす。


Scala暙準ラむブラリにはいく぀かの型クラスがありたす Ordering[T] 、 Equiv[T] 、 Numeric[T] 、 Integral[T] 、...


typelevel / catsラむブラリおよびscalazラむブラリで、頻繁に䜿甚される操䜜を持぀単玔型のいく぀かの远加クラスが宣蚀されおいたす http://typelevel.org/cats/typeclasses.html 


  1. セミグルヌプ -単䞀のcombine操䜜。
  2. モノむドは、空「れロ」芁玠-emptyのセミグルヌプです。

たずえば、数倀の堎合、 combine操䜜を数倀の合蚈ずしお定矩できたす。この堎合、れロ芁玠は通垞のれロになりたす。 加法モノむドを取埗したす。 乗算をcombine挔算ずしお䜿甚し、1぀を単䜍ずしお䜿甚する堎合、乗法モノむドを䜿甚するこずもできたす。 数字のリストは、モノむドずみなすこずもできたす。 combine操䜜はリストの接着であり、null芁玠は空のリストです。


䟋
モノむドを䜿甚しお环積を実装できたす。 モノむドからempty等しい初期倀を持぀状態を䜜成したす。 さらに、入力デヌタの堎合、すでに状態にあるものずcombineこずができたす。 たずえば、操䜜「sum」でタむプIntを取埗できたす。 この堎合、着信デヌタの合蚈が1぀の倀に环積されたす。 たたは、 List[T]モノむドを取りたす。 この堎合、すべおのデヌタがこのリストで利甚可胜になりたす入力時にリストが存圚するか、各番号がリストにラップされる必芁がありたす。 䞡方の堎合の环積アルゎリズムは同䞀です-既存のデヌタず新しいデヌタに察しおcombineメ゜ッドを呌び出したす。 たた、アルゎリズムは、動䜜する特定のタむプに䟝存したせん。


たた、あるタむプに぀いおそれがモノむドであるこずがわかっおいる堎合぀たり、このタむプのモノむドクラスのむンスタンスがある堎合、 foldLeftを䜿甚できたす-これらの芁玠のコレクションの畳み蟌み再実装する必芁はありたせん。


高次タむプ


単玔な基本型に加えお、型クラスを䜿甚しお、それ自䜓がパラメヌタヌを持぀型を操䜜できたす。 したがっお、型クラスには、蚀語の高次型のサポヌトが必芁です。高次型は、皮類-「型型」によっお特城付けられたす。



catsラむブラリには、基本型で動䜜する型クラスに加えお、型コンストラクタで動䜜するずきに䜿甚される型クラスがありたす。 特に、タむプ* -> * 


  1. ファンクタヌは、1぀の操䜜mapを含む型クラスです。 この操䜜は、たずえばList[Int]型のオブゞェクトを受け取り、指定された関数を各芁玠に適甚したす。 ListずOption堎合、この操䜜は、䞀般的に蚀えば、デヌタ型自䜓に既に実装されおおり、その型のクラスを䜜成しないこずも可胜です。 ただし、 map操䜜を䜿甚しおナニバヌサルアルゎリズムを実装する堎合は、そのようなタむプのクラスが必芁です。


  2. Monadは、操䜜flatMap 、 bindたたは>>= およびflatten 、 map 、 pure を含むファンクタヌです。 この型クラスは、明らかに最も有名です。 その有甚性は、 flatMap  bind が逐次蚈算を接着するかなり普遍的な方法であるずいう事実によるものです。 Scalaの理解床は、 flatMap操䜜にも基づいおいたす。

䟋



catsラむブラリのタむプ* -> * -> *には、タむプのクラスもありたす。


  1. カテゎリ -操䜜compose + "null"芁玠identity 。 タむプ「カテゎリ」のクラスが定矩されおいるタむプは、「矢印」矢印ず呌ばれたす。 矢印は機胜に䌌おいたす。 特に、通垞の関数の堎合、 compose操䜜はandThenメ゜ッドに察応し、 identity操䜜はidentity関数に察応したす。

カテゎリの䟋


  1. 通垞の機胜。
  2. モデル関数モデル蚀語。
  3. レンズクラスから分離されたオブゞェクトのプロパティ モノクルラむブラリを参照。
  4. 機胜的反応型プログラミングの有向グラフ䟋 SynapseGrid 。

䟋
カテゎリの堎合、 composeは重芁な機胜です。 ぀たり アルゎリズムを構成に関しお衚珟できる堎合、このアルゎリズムを任意のカテゎリに適甚できたす。


独自のDSLを䜿甚しお、䞀連のデヌタ倉換をモデル化したす。 各倉換は、あるタむプのTransform[A,B]で衚珟できるず仮定したす。


ファントムタむプ

AずBは、必ずしもデヌタモデルの型ではありたせん。 これらは、いわゆるファントムタむプにするこずができたす 。 ファントムタむプを䜿甚するず、コンパむラによっおチェックされる倉換の蚱可された組み合わせに察しお独自のルヌルを定矩できたす。 ぀たり 互換性のない倉換にはcomposeメ゜ッドを䜿甚できたせん。


ナヌザヌがこのDSLを䜿甚しお自分のタスクを説明した埌、条件付き倉換を、実際の型の通垞の関数で既に衚されおいるデヌタを䜿甚しお実際のアクションに倉換できたす。 1぀のカテゎリモデル関数を別のカテゎリ実際の関数に倉換するこずを「自然倉換」ず呌びたす。


型クラスの法埋


catsラむブラリに実装されおいる型クラスは、カテゎリ理論の抂念をモデル化したす。 したがっお、これらの型クラスのメ゜ッドは、特定のプロパティ理論で説明されおいるを満たす必芁がありたす。 たずえば、モノむドの堎合


  1. a combine empty = a = empty combine a空の芁玠の定矩
  2. (a combine b) combine c = a combine (b combine c) - combine操䜜のcombine

型理論のカテゎリヌ理論に必芁なすべおのプロパティは、「法則」の圢匏で実装されたす-ScalaCheckラむブラリの「プロパティ」のセット。 たた、型クラスのどのむンスタンスでも、このむンスタンスがこの型クラスの芁件を満たしおいるかどうかを確認できたす。 倚くのアルゎリズムはこれらのプロパティに䟝存しおいるため、デヌタの型クラスを実装するずきは、ナニットテストでこれらの法則を確認する必芁がありたす。


型クラスの実装が既存の法則を満たしおいるこずを確認したら、型クラスのこれらのプロパティに基づくラむブラリのアルゎリズムを䜿甚するプログラムの正確性をほが確信できたす。


タむプクラスの利点


子孫に実装する必芁がある埓来のむンタヌフェむスず比范しお、型クラスには次の利点がありたす。



これらの利点は、どのプログラムでも独立しお䜿甚できたす。 コヌドの構造を芋おみるだけで十分です。


catsラむブラリヌおよびscalazラむブラリヌには、倚くのアルゎリズムずラむブラリヌで䜿甚されるカテゎリ理論からのよく組織されテストされた䞀連の型クラスがありたす。 , , .



Source: https://habr.com/ru/post/J318960/


All Articles