関数型プログラミングのためのPythonラむブラリf

こんにちは同僚


簡朔な名前f持぀Pythonのラむブラリに぀いお説明したす 。 これは、機胜的なスタむルで問題を解決するための関数ずクラスを備えた小さなパッケヌゞです。


-䜕、Python甚の別の機胜ラむブラリですか 著者は、 fn.pyがあり、䞀般にこれらの機胜的なクラフトが100䞇個あるこずを知っおいたすか


-はい、知っおいたす。


ラむブラリの倖芳の理由


私はかなり長い間Pythonを緎習しおきたしたが、数幎前、関数型プログラミング、特にKlyuchに真剣に倢䞭になりたした。 FPで採甚されたいく぀かのアプロヌチは私に非垞に匷い印象を䞎えたので、私はそれらを日垞の開発に移したいず思いたした。


ある蚀語のパタヌンが別の蚀語のパタヌンに倧たかに埋め蟌たれおいる堎合、その原則ずコヌディング合意を考慮せずにアプロヌチを受け入れないこずを匷調したす。 FPがどれほど奜きであっおも、これを機胜的なスタむルずしお流そうずするために、倧量のマップずラムダに悩たされおいたす。


そのため、同僚の抵抗に合わないように機胜を敎えようずしたした。 たずえば、FPに粟通しおいない人の理解を促進するために、マップずリダクションの代わりに条件付きの暙準サむクルを䜿甚したす。


その結果、図曞通の䞀郚の郚分は戊闘プロゞェクトにあり、おそらくはただ䜿甚䞭でした。 最初に、それらをプロゞェクトからプロゞェクトにコピヌし、次に関数ずスニペットのファむルダンプを開始し、最埌にラむブラリ、Pypiのパッケヌゞ、およびドキュメントですべおを蚭蚈したした。


䞀般的な情報


ラむブラリは玔粋なPythonで曞かれおおり、以䞋を含むすべおのOSで動䜜したす ノィンドゥスに。 Pythonの䞡方のブランチがサポヌトされおいたす。 具䜓的には、バヌゞョン2.6、2.7、および3.5をチェックしたした。 他のバヌゞョンで問題が発生した堎合はお知らせください。 唯䞀の䟝存関係は、䞡方のブランチのすぐ䞋にあるアゞャむル開発甚のsixパッケヌゞです。


ラむブラリは、pipを介しお暙準的な方法でむンストヌルされたす。


 pip install f 

すべおの関数ずクラスは、headモゞュヌルで利甚できたす。 それは芚えおおく必芁がないこずを意味したす
゚ンティティぞのパス


 import f f.pcall(...) f.maybe(...) f.io_wraps(...) fL[1, 2, 3] 

パッケヌゞには、次のサブシステムが搭茉されおいたす。



以䞋のセクションでは、コメント付きのコヌド䟋を瀺したす。


機胜


別の゚コシステムからPythonに転送した最初の関数は、Lua蚀語からのpcall 。 私は数幎前にプログラミングしたしたが、蚀語は機胜しおいたせんが、喜んでいたす。


pcall保護された呌び出し関数は、別の関数を取り、ペア(err, result)を返したす。errぱラヌで、 result空、たたはその逆です。 JavascriptやGowなどの他の蚀語でのこのアプロヌチには粟通しおいたす。


 import f f.pcall(lambda a, b: a / b, 4, 2) >>> (None, 2) f.pcall(lambda a, b: a / b, 4, 0) >>> (ZeroDivisionError('integer division or modulo by zero'), None) 

この関数を、䟋倖をスロヌする既に蚘述された関数のデコレヌタヌずしお䜿甚するず䟿利です。


 @f.pcall_wraps def func(a, b): return a / b func(4, 2) >>> (None, 2) func(4, 0) >>> (ZeroDivisionError('integer division or modulo by zero'), None) 

砎壊的な構文を䜿甚するず、眲名レベルで結果を解凍できたす。


 def process((err, result)): if err: logger.exception(err) return 0 return result + 42 process(func(4, 2)) 

残念ながら、砎壊的な構文は3番目のPythonで削陀されおいたす。 手動で解凍する必芁がありたす。


興味深いこずに、ペア(err, result)は、 Eitherモナドに他なりたせん。


以䞋は、より珟実的なpcall䟋です。 倚くの堎合、HTTP芁求を䜜成し、ゞェむ゜ンからデヌタ構造を取埗する必芁がありたす。 リク゚スト䞭に倚くの゚ラヌが発生する可胜性がありたす。



4぀の䟋倖をキャッチしおtryで呌び出しをラップするずいうこずは、コヌドを完党に読み取り䞍可胜にするこずを意味したす。 遅かれ早かれ、䜕かを傍受するのを忘れ、プログラムがクラッシュしたす。 以䞋は、ほが実際のコヌドの䟋です。 ロヌカルレストサヌビスからナヌザヌを取埗したす。 結果は垞にペアになりたす。


 @f.pcall_wraps def get_user(use_id): resp = requests.get("http://local.auth.server", params={"id": user_id}, timeout=3) if not resp.ok: raise IOError("<log HTTP code and body here>") data = resp.json() if "error" in data: raise BusinesException("<log here data>") return data 

他のラむブラリ関数を怜蚎しおください。 f.achainずf.ichainを匷調しf.achainずf.ichainたす。 どちらも、チェヌン内のオブゞェクトから安党にデヌタを取埗するように蚭蚈されおいたす。


次のモデルのdjangoがあるずしたす


 Order => Office => Department => Chief 

同時に、すべおのフィヌルドがnot nullはnot null隣接するフィヌルドを安党に確認できたす。


 order = Order.objects.get(id=42) boss_name = order.office.department.chief.name 

はい、私はselect_related知っおいたすが、それは問題ではありたせん。 この状況は、ORMだけでなく、他のクラス構造にも圓おはたりたす。


ある顧客がいく぀かのリンクを空にするこずを芁求するたで、それは私たちのプロゞェクトにありたした。これらは圌のビゞネスの特城だからです。 デヌタベヌス内のフィヌルドをnullable 、簡単に降りるこずができお嬉しかったです。 もちろん、急ぎのために、空のリンクを持぀モデルの単䜓テストを䜜成したせんでした。叀いテストでは、モデルは正しく入力されおいたした。 クラむアントは曎新されたモデルで䜜業を開始し、゚ラヌを受け取りたした。


f.achain関数は、属性チェヌンを安党に走査したす。


 f.achain(model, 'office', 'department', 'chief', 'name') >>> John 

チェヌンが壊れおいる堎合フィヌルドがNone、存圚しない、結果はNoneになりたす。


f.ichainアナログf.ichainは、䞀連のむンデックスを実行したす。 圌女は蟞曞、リスト、およびタプルを䜿甚したす。 この関数は、jasonから取埗したデヌタを操䜜するのに䟿利です。


 data = json.loads('''{"result": [{"kids": [{"age": 7, "name": "Leo"}, {"age": 1, "name": "Ann"}], "name": "Ivan"}, {"kids": null, "name": "Juan"}]}''') f.ichain(data, 'result', 0, 'kids', 0, 'age') >>> 7 f.ichain(data, 'result', 0, 'kids', 42, 'dunno') >> None 

私は䞡方の機胜をクラッチからget-in 。クラッチの祖先はget-inず呌ばget-inたす。 䟿利な点は、マむクロサヌバヌアヌキテクチャでは、応答の構造が絶えず倉化しおおり、垞識に察応しおいない堎合があるこずです。


たずえば、応答には、ネストされたフィヌルドを持぀ナヌザヌオブゞェクトフィヌルドがありたす。 ただし、䜕らかの理由でナヌザヌがいない堎合、フィヌルドは空のオブゞェクトではなく、なしになりたす。 次のようなUい構造


 data.get('user', {]}).get('address', {}).get('street', '<unknown>') 

私たちのバヌゞョンは読みやすいです


 f.ichain(data, 'user', 'address', 'street') or '<unknown>' 

Kluzhaからラむブラリf枡される2぀のスレッドマクロ ->および->> 。 ラむブラリでは、それらはf.arr1およびf.arr2ず呌ばれたす。 䞡方ずも、初期倀を関数圢匏に枡したす 。 Lispのこの甚語は、埌で蚈算される匏を意味したす。


蚀い換えれば、 フォヌムはfunc関数たたはフォヌムのタプル(func, arg1, arg2, ...)いずれか(func, arg1, arg2, ...) 。 このフォヌムは、凍結匏のような堎所に枡しお、埌で倉曎しお蚈算できたす。 Lispのマクロのようなものが刀明したすが、非垞に悲惚です。


f.arr1は倀およびさらに結果を最初の倀f.arr1眮き換えたす
フォヌム匕数


 f.arr1( -42, #   (lambda a, b: a + b, 2), #  abs, #  str, #  ) >>> "40" 

f.arr2は同じこずを行いたすが、倀をフォヌムの最埌にf.arr2したす。


 f.arr2( -2, abs, (lambda a, b: a + b, 2), str, ("000".replace, "0") ) >>> "444" 

次に、 f.comp関数は関数の構成を返したす。


 comp = f.comp(abs, (lambda x: x * 2), str) comp(-42) >>> "84" 

f.every_predはスヌパヌ述語を構築したす。 これは、すべおの内郚述語が真である堎合にのみ真ずなる述語です。


 pred1 = f.p_gt(0) #   pred2 = f.p_even #  pred3 = f.p_not_eq(666) #   666 every = f.every_pred(pred1, pred2, pred3) result = filter(every, (-1, 1, -2, 2, 3, 4, 666, -3, 1, 2)) tuple(result) >>> (2, 4, 2) 

スヌパヌ述語は怠laです。最初の停の倀で蚈算の連鎖を䞭断したす。 䞊蚘の䟋では、 predicate.pyモゞュヌルのpredicate.pyが䜿甚されおいたす。これに぀いおは埌で説明したす。


f.transduce関数は、Claudeのトランスデュヌサヌトランスフォヌマヌパタヌンを実装しようずする玠朎な詊みです。 芁するに、 transducerはmap機胜ずreduce機胜の組み合わせです。 それらの重ね合わせにより、「䞭間デヌタのないものからあらゆるものぞ」ずいう原則に埓っお倉換が行われたす。


 f.transduce( (lambda x: x + 1), (lambda res, item: res + str(item)), (1, 2, 3), "" ) >>> "234" 

関数のモゞュヌルf.nth f.nthずその同矩語コレクション芁玠ぞの安党なアクセスのためのf.first 、 f.secondおよびf.third 


 f.first((1, 2, 3)) >>> 1 f.second((1, 2, 3)) >>> 2 f.third((1, 2, 3)) >>> 3 f.nth(0, [1, 2, 3]) >>> 1 f.nth(9, [1, 2, 3]) >>> None 

述語


は、trueたたはfalseを返す匏です。 述語は、数孊、論理、および関数型プログラミングで䜿甚されたす。 倚くの堎合、述語は高階関数の倉数ずしお枡されたす。


ラむブラリに最も必芁な述語のいく぀かを远加したした。 述郚の動䜜が最初の匕数に䟝存する堎合、述郚は単項 パラメヌタヌなしおよび二項 たたはパラメトリックになりたす。


単項述語を䜿甚した䟋を怜蚎しおください。


 f.p_str("test") >>> True f.p_str(0) >>> False f.p_str(u"test") >>> True #  ,    int  float  f.p_num(1), f.p_num(1.0) >>> True, True f.p_list([]) >>> True f.p_truth(1) >>> True f.p_truth(None) >>> False f.p_none(None) >>> True 

バむナリになりたした。 䜕かがれロより倧きいず䞻匵する新しい述語を䜜成したす。 いったい䜕 ただ知られおいない、これは抜象化です。


 p = f.p_gt(0) 

次に、述語を䜿甚しお、任意の倀を確認したす。


 p(1), p(100), p(0), p(-1) >>> True, True, False, False 

類掚によっお


 # -    : p = f.p_gte(0) p(0), p(1), p(-1) >>> True, True, False #    : p = f.p_eq(42) p(42), p(False) >>> True, False #    : ob1 = object() p = f.p_is(ob1) p(object()) >>> False p(ob1) >>> True #      : p = f.p_in((1, 2, 3)) p(1), p(3) >>> True, True p(4) >>> False 

すべおの述語の䟋は瀺したせんが、それは退屈で長くなりたす。 述語は、合成関数f.comp 、スヌパヌ述語f.every_pred 、組み蟌みfilter関数、および以䞋で説明する汎甚f.every_predず完党にf.compたす。


ゞェネリック


ゞェネリックgeneral、generalizedは、結果を蚈算するためのいく぀かの戊略を持぀、呌び出されるオブゞェクトです。 戊略の遞択は、入力パラメヌタヌ構成、タむプ、たたは倀に基づいお決定されたす。 ゞェネリックは、枡されたパラメヌタヌに察しお他に芋぀からない堎合、デフォルト戊略の存圚を想定したす。


Pythonでは、すぐに䜿甚できるゞェネリックはなく、特に必芁ありたせん。 Pythonは、入力倀の関数を遞択する独自のシステムを構築するのに十分な柔軟性を備えおいたす。 それでも、Common Lispでのゞェネリックの実装が非垞に奜きだったので、スポヌツの関心からラむブラリで同様のこずをするこずにしたした。


こんな感じです。 たず、ゞェネリックのむンスタンスを䜜成したす。


 gen = f.Generic() 

次に、特定のハンドラヌで拡匵したす。 .extendデコレヌタは、このハンドラヌの䞀連の述語を匕数ごずに1぀受け取りたす。


 @gen.extend(f.p_int, f.p_str) def handler1(x, y): return str(x) + y @gen.extend(f.p_int, f.p_int) def handler2(x, y): return x + y @gen.extend(f.p_str, f.p_str) def handler3(x, y): return x + y + x + y @gen.extend(f.p_str) def handler4(x): return "-".join(reversed(x)) @gen.extend() def handler5(): return 42 

内郚のロゞックは単玔です。デコレヌタは、割り圓おられた述語ずずもに関数を内郚蟞曞にファむルしたす。 これで、ゞェネリックは任意の匕数で呌び出すこずができたす。 呌び出されるず、関数は同じ数の予枬子で怜玢されたす。 各述語が察応する匕数に察しおtrueを返す堎合、戊略が芋぀かったず芋なされたす。 芋぀かった関数を呌び出した結果が返されたす


 gen(1, "2") >>> "12" gen(1, 2) >>> 3 gen("fiz", "baz") >>> "fizbazfizbaz" gen("hello") >>> "olleh" gen() >>> 42 

戊略が出ない堎合はどうなりたすか デフォルトのハンドラヌが蚭定されおいるかどうかによっお異なりたす。 このようなハンドラヌは、任意の数の匕数を満たす準備ができおいる必芁がありたす。


 gen(1, 2, 3, 4) >>> TypeError exception goes here... @gen.default def default_handler(*args): return "default" gen(1, 2, 3, 4) >>> "default" 

装食埌、関数はゞェネリックのむンスタンスになりたす。 興味深いトリックは、ある戊略の実行を別の戊略に移すこずができるずいうこずです。 Claude、Erlang、Haskellのように、いく぀かのボディで機胜するこずがわかりたす。


Noneを枡すず、以䞋のハンドラヌが呌び出されたす。 ただし、内郚では2぀のintを持぀別のハンドラヌにリダむレクトされたす。これはhandler2です。 これにより、匕数の合蚈が返されたす。


 @gen.extend(f.p_none) def handler6(x): return gen(1, 2) gen(None) >>> 3 

コレクション


このラむブラリは、リスト、タプル、蟞曞、およびセットに基づいた「匷化された」コレクションを提䟛したす。 改善ずは、各コレクションの動䜜における远加のメ゜ッドずいく぀かの機胜を意味したす。


改善されたコレクションは、クラスを呌び出すこずによっお通垞のコレクションから䜜成されるか、角括匧で囲たれた特別な構文によっお䜜成されたす。


 fL[1, 2, 3] #  f.List([1, 2, 3]) >>> List[1, 2, 3] fT[1, 2, 3] #  f.Tuple([1, 2, 3]) >>> Tuple(1, 2, 3) fS[1, 2, 3] #  f.Set((1, 2, 3)) >>> Set{1, 2, 3} fD[1: 2, 2: 3] >>> Dict{1: 2, 2: 3} #  f.Dict({1: 2, 2: 3}) 

コレクションには、 .join 、 .foreach 、 .map 、 .filter 、 .reduce 、 .sumたす。


リストずタプルは、さらに.reversed 、 .sorted 、 .group 、 .distinctおよび.applyたす。


メ゜ッドを䜿甚するず、関数に枡さずにコレクションから結果を取埗できたす。


 l1 = fL[1, 2, 3] l1.map(str).join("-") >>> "1-2-3" 

 result = [] def collect(x, delta=0): result.append(x + delta) l1.foreach(collect, delta=1) result == [2, 3, 4] >>> True 

 l1.group(2) >>> List[List[1, 2], List[3]] 

私は各メ゜ッドのリストを退屈させたせん。垌望する人はコメント付きで゜ヌスコヌドを芋るこずができたす。


メ゜ッドが同じコレクションの新しいむンスタンスを返すこずが重芁です。 これにより、圌女が誀っお倉わる可胜性が䜎くなりたす。 操䜜.mapたたはリストの他の操䜜は、リスト、タプル、タプルなどを返したす。


 fL[1, 2, 3].filter(f.p_even) >>> List[2] 

 fS[1, 2, 3].filter(f.p_even) >>> Set{2} 

蟞曞は、私がい぀も倢芋おいたペア(, )反埩凊理し(, ) 。


 fD[1: 1, 2: 2, 0: 2].filter(lambda (k, v): k + v == 2) >>> Dict{0: 2, 1: 1} 

改善されたコレクションは、他のコレクションず積み重ねるこずができたす。 結果は、この巊タむプの新しいコレクションになりたす。


 #   fD(a=1, b=2, c=3) + {"d": 4, "e": 5, "f": 5} >>> Dict{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4, 'f': 5} #  +   fS[1, 2, 3] + ["a", 1, "b", 3, "c"] >>> Set{'a', 1, 2, 3, 'c', 'b'} #     fL[1, 2, 3] + (4, ) List[1, 2, 3, 4] 

どのコレクションも別のコレクションに切り替えるこずができたす。


 fL["a", 1, "b", 2].group(2).D() >>> Dict{"a": 1, "b": 2} fL[1, 2, 3, 3, 2, 1].S().T() >>> Tuple[1, 2, 3] 

コンボ


 fL("abc").map(ord).map(str).reversed().join("-") >>> "99-98-97" 

 def pred(pair): k, v = pair return k == "1" and v == "2" fL[4, 3, 2, 1].map(str).reversed() \ .group(2).Dict().filter(pred) >>> Dict{"1": "2"} 

モナド


ラむブラリの最埌で最も難しいセクション。 モナドに関する䞀連の蚘事を読んだ埌、私もそれらをラむブラリに远加しようず思いたした。 同時に、圌は次の逞脱を蚱可したした。



たぶん


たぶん、モナドはオプションずしおも知られおいたす。 このクラスのモナドは、2぀のむンスタンスで衚されたす。Just たたはSomeは、興味のある肯定的な結果のリポゞトリです。 䜕も他の蚀語では-なし -空の結果。


簡単な䟋。 モナドコンストラクタヌを定矩する- スカラヌフラット倀をモナドに倉換するオブゞェクト


 MaybeInt = f.maybe(f.p_int) 

別の方法では、 unit 、たたはモナドナニットず呌ばれたす。 モナド倀を取埗したす


 MaybeInt(2) >>> Just[2] MaybeInt("not an int") >>> Nothing 

良い結果になるのは、intのテストに合栌するこずだけです。 それでは、 モナドパむプラむンを実際に詊しおみたしょう。


 MaybeInt(2) >> (lambda x: MaybeInt(x + 2)) >>> Just[4] MaybeInt(2) >> (lambda x: f.Nothing()) >> (lambda x: MaybeInt(x + 2)) >>> Nothing 

䟋からわかるように、 Nothingチェヌンの実行を䞭断しNothing 。 完党に正確に蚀うず、チェヌンは途切れず、最埌たで進み、各ステップでNothing返されNothing 。


モナドデコレヌタで任意の関数をカバヌしお、そこからスカラヌのモナド衚珟を取埗できたす。 以䞋の䟋では、デコレヌタヌは、戻り倀のintのみが成功ず芋なされるようにしたす。この倀はJust 、他のすべおはNothingたす。


 @f.maybe_wraps(f.p_num) def mdiv(a, b): if b: return a / b else: return None mdiv(4, 2) >>> Just[2] mdiv(4, 0) >>> Nothing 

>>挔算子は、 モナドバむンディングずも呌ばれ、 .bindメ゜ッドによっお呌び出されたす。


 MaybeInt(2).bind(lambda x: MaybeInt(x + 1)) >>> Just[3] 

>>ず.bindメ゜ッドは、関数だけでなく、 関数の圢をずるこずもできたす。


 MaybeInt(6) >> (mdiv, 2) >>> Just[3] MaybeInt(6).bind(mdiv, 2) >>> Just[3] 

モナドからスカラヌ倀を解攟するには、 .getメ゜ッドを䜿甚したす。 モナドの叀兞的な定矩には含たれおおらず、䞀皮のinであるこずに泚意するこずが重芁です。 .getメ゜ッドは、厳密にパむプラむンの最埌にある必芁がありたす。


 m = MaybeInt(2) >> (lambda x: MaybeInt(x + 2)) m.get() >>> 3 

どちらか


このモナドは前のものを拡匵したす。 たぶん問題は吊定的な結果が砎棄されるこずですが、私たちは垞に理由を知りたいです。 どちらも、巊ず右、巊ず右の倀のサブタむプで構成されたす。 巊の倀は負のケヌスを担圓し、右の倀は正のケヌスを担圓したす。


このルヌルは、「私たちの原因は正しい぀たり、真」ずいうフレヌズで芚えやすいです。 英語の正しい蚀葉は「忠実」も意味したす。


そしお、ここに過去からのフラッシュバックがありたす同意しお、それは蚘事の冒頭からカップル(err, result)を思い出させたすか Javascriptでのコヌルバック Gowの呌び出し結果異なる順序でのみ


それは同じです。 これらはすべおモナドですが、コンテナや数孊的な装眮なしでは装食されおいたせん。


Eitherモナドは、䞻に゚ラヌをキャッチするために䜿甚されたす。 誀った倀は巊に進み、組立ラむンの結果になりたす。 正しい結果は、次の蚈算のために右偎にスクランブルされたす。


Bothモナドコンストラクタヌは、巊の倀ず右の2぀の述語を受け入れたす。 以䞋の䟋では、文字列倀は巊の倀に、数倀は右に移動したす。


 EitherStrNum = f.either(f.p_str, f.p_num) EitherStrNum("error") >>> Left[error] EitherStrNum(42) >>> Right[42] 

コンベアを確認したす。


 EitherStrNum(1) >> (lambda x: EitherStrNum(x + 1)) >>> Right[2] EitherStrNum(1) >> (lambda x: EitherStrNum("error")) \ >> (lambda x: EitherStrNum(x + 1)) >>> Left[error] 

f.either_wrapsデコレヌタは、関数をモナドコンストラクタにしたす。


 @f.either_wraps(f.p_str, f.p_num) def ediv(a, b): if b == 0: return "Div by zero: %s / %s" % (a, b) else: return a / b @f.either_wraps(f.p_str, f.p_num) def esqrt(a): if a < 0: return "Negative number: %s" % a else: return math.sqrt(a) EitherStrNum(16) >> (ediv, 4) >> esqrt >>> Right[2.0] EitherStrNum(16) >> (ediv, 0) >> esqrt >>> Left[Div by zero: 16 / 0] 

IO


IOモナド入出力は、ファむルの読み取り、キヌボヌド入力、画面ぞの印刷など、デヌタの入出力を分離したす。 たずえば、ナヌザヌ名を芁求する必芁がありたす。 モナドがなければ、 raw_inputですが、これは抜象化を枛らし、副䜜甚でコヌドを詰たらせたす。


キヌボヌド入力を分離する方法は次のずおりです。


 IoPrompt = f.io(lambda prompt: raw_input(prompt)) IoPrompt("Your name: ") #  .   "Ivan"   RET >>> IO[Ivan] 

モナドを取埗したので、それをさらに組立ラむンに転送できたす。 次の䟋では、名前を入力しお衚瀺したす。 f.io_wrapsデコレヌタは、関数をモナドコンストラクタヌに倉換したす。


 import sys @f.io_wraps def input(msg): return raw_input(msg) @f.io_wraps def write(text, chan): chan.write(text) input("name: ") >> (write, sys.stdout) >>> name: Ivan #   >>> Ivan #   >>> IO[None] #  

゚ラヌ


゚ラヌモナド、Try゚ラヌ、詊行も実甚的な芳点から非垞に䟿利です。 䟋倖を分離し、蚈算の結果が内郚に正しい倀を持぀Successむンスタンス、たたはFailtureれた䟋倖を持぀Failtureであるこずを確認したす。


Maybe andeitherず同様に、モナドパむプラむンは肯定的な結果のためにのみ実行されたす。


, . Success , Failture :


 Error = f.error(lambda a, b: a / b) Error(4, 2) >>> Success[2] Error(4, 0) >>> Failture[integer division or modulo by zero] 

.get Failture . ? .recover :


 Error(4, 0).get() ZeroDivisionError: integer division or modulo by zero # value variant Error(4, 0).recover(ZeroDivisionError, 42) Success[2] 

( ), . Success . . , Success . :


 def handler(e): logger.exception(e) return 0 Error(4, 0).recover((ZeroDivisionError, TypeError), handler) >>> Success[0] 

. :


 @f.error_wraps def tdiv(a, b): return a / b @f.error_wraps def tsqrt(a): return math.sqrt(a) tdiv(16, 4) >> tsqrt >>> Success[2.0] tsqrt(16).bind(tdiv, 2) >>> Success[2.0] 


, . , , ? ?


do-, . :


 def mfunc1(a): return f.Just(a) def mfunc2(a): return f.Just(a + 1) def mfunc3(a, b): return f.Just(a + b) mfunc1(1) >> (lambda x: mfunc2(x) >> (lambda y: mfunc3(x, y))) # 1 2 1 2 >>> Just[3] 

, mfunc3 , . x y . .


おわりに


, f . , . , . — .


. — . Pypi .


, .


. ご枅聎ありがずうございたした。



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


All Articles