Upgrade to Pro — share decks privately, control downloads, hide ads and more …

エンジニアにとってコードと並んで重要な「データ」のお話 - データが動くとコードが見える:関数...

エンジニアにとってコードと並んで重要な「データ」のお話 - データが動くとコードが見える:関数型=データフロー入門

2025/11/10(月) Hello LT world『みんなで語る!好きと学びのアウトプット LT Night』
https://findy.connpass.com/event/372581/

登壇時の発表資料です。

Avatar for Izumu KUSUNOKI

Izumu KUSUNOKI

November 09, 2025
Tweet

More Decks by Izumu KUSUNOKI

Other Decks in Technology

Transcript

  1. ܕͰೖྗͱग़ྗΛ໌֬ʹ͢Δ # Cart(注文) → Quote(見積書) を作成する純粋関数 def to_quote(c: Cart) ->

    Quote: lines = tuple( QuoteLine(sku=i.sku, qty=i.qty, subtotal=i.price * i.qty) for i in c.items ) total = sum(l.subtotal for l in lines) return Quote(lines=lines, total=total) # 境界で副作用注入(ID 生成など) def confirm(q: Quote, gen_id: Callable[[], str]) -> Order: return Order(id=gen_id(), total=q.total) ϙΠϯτɿ෭࡞༻ʢ*%ੜ੒ͳͲʣ͸Ҿ਺Ͱ஫ೖͯ͠ڥքʹ௥͍ग़͢ 純粋関数として実装することで → テストが容易になり、予測可能性が向上する 副作用を引数化することで → 関数の振る舞いを外部から制御可能にする ύΠϓϥΠϯͷྫ ɾ ɾ ֤Ϋϥε͸!EBUBDMBTT΍1ZEBOUJDͳͲʹΑΔఆٛͩͱ͠·͢
  2. ܕͰೖྗͱग़ྗΛ໌֬ʹ͢Δ # 合成(pipe)によって複雑な純粋関数のロジックを組み立てられる A = TypeVar("A"), B = TypeVar("B"), C

    = TypeVar("C") def pipe(f: Callable[[A], B], g: Callable[[B], C]) -> Callable[[A], C]: return lambda a: g(f(a)) def checkout(gen_id: Callable[[], str]) -> Callable[[Cart], Order]: return pipe(to_quote, lambda q: confirm(q, gen_id)) # --- 使い方例(決定的テストが容易) --- if __name__ == "__main__": fixed = lambda: "id" cart = Cart(items=(CartItem("A", price=100, qty=2),)) order = checkout(fixed)(cart) # Order(id='id', total=200) print(order) ύΠϓϥΠϯͷྫ
  3. ঢ়ଶભҠ΋ܕͰද͢ OrderState = Union[Draft, Confirmed] def confirm_state(d: Draft, id: str)

    -> Confirmed: return Confirmed(kind="Confirmed", id=id, total=d.total) def render_state(s: OrderState) -> str: match s: case Draft(kind="Draft", total=t): return f"Draft: {t}" case Confirmed(kind="Confirmed", id=i, total=t): return f"Confirmed: {i} / {t}" ύΠϓϥΠϯͷྫ ɾ໢ཏੑνΣοΫ͕ޮ͘ʹ౸ୡෆೳঢ়ଶΛܕͰ๷ࢭ ɾঢ়ଶભҠ͕໌ࣔతʹͳΓɺίʔυͷҙਤ͕໌֬ʹͳΔ
  4. σʔλج൫Ͱ΋ಉ͡ൃ૝Λৗ༻͍ͯ͠Δ ೖྗεΩʔϚ ˠ७ਮม׵ ˠग़ྗεΩʔϚ ෭࡞༻ʹ*0͸ڥք σʔλΤϯδχΞϦϯάʹ͓͍ͯ΋ؔ਺ܕͷߟ͑ํ͕ඇৗʹ༗ޮʜ ͱ͍͏͔ɺ&5-ʢ&YUSBDU 5SBOTGPSN -PBEʣϓϩηε͸ɺ ·͞ʹσʔλͷܗΛม׵͢ΔύΠϓϥΠϯͦͷ΋ͷɻ

    ͜Ε͕ࠓճ঺հ͔ͨͬͨ͜͠ͱɻ ڞ௨͢Δݪଇ ܾఆతͳม׵ ಉ͡ೖྗ͔Βৗʹಉ͡ग़ྗΛੜ੒ ႈ౳ੑ Կ౓࣮ߦͯ͠΋݁Ռ͕มΘΒͳ͍ ςελϒϧ ೖྗͱظ଴ग़ྗΛఆٛ͢Ε͹ɺςετ͕༰қ <ຊ୊>σʔλΤϯδχΞ࣮຿ɿؔ਺ܕ&5-&-5 εΩʔϚϑΝʔετʹɺႈ౳ͳσʔλม׵ͷύʔπΛ૊Έ߹Θ͍ͤͯ͘ͷ͕σʔλύΠϓϥΠϯ։ൃ
  5. ؔ਺ܕσʔλϑϩʔΛମݱ͢Δπʔϧ %BHTUFS͸ʮΞηοτத৺ʯͷࢥߟͰσʔλύΠϓϥΠϯΛߏங @assetdef daily_sales(...) -> DataFrame: # 純粋な変換ロジックのみを記述 # 返り値の型に基づいて、IO

    Managerが自動的に永続化 return pd.DataFrame(...) @asset(deps=[daily_sales]) def weekly_sales(daily_sales: DataFrame) -> DataFrame: # daily_salesの読み込みはIO Managerが自動処理 # 変換ロジックのみに集中できる return daily_sales.resample('W').sum() *0ͷ੹຿Λ%BHTUFSଆ͕ٵऩ Ξηοτϕʔε ؔ਺ܕσʔλϑϩʔ ೖྗΞηοτ ˠ७ਮม׵ ˠग़ྗΞηοτ ෭࡞༻ʢ*0ʣ͸ڥքʢ*0.BOBHFSʣ΁ %BHTUFS ਪ͠ ͷΞηοτϕʔεΞϓϩʔν  ७ਮͳม׵ϩδοΫͷΈهड़ r ར༻ऀ͸ؔ਺ͷத਎ʢσʔλม׵ʣ͚ͩΛ࣮૷͢Ε͹͍͍  *0 .BOBHFS ͕ӬଓԽΛ୲౰ r ϑΝΠϧॻ͖ࠐΈɺ%#઀ଓͳͲͷ෭࡞༻͸%BHTUFS͕ॲཧ  ฦΓ஋ͷܕʹجͮࣗ͘ಈॲཧ r ܕ৘ใ͔Βద੾ͳӬଓԽํ๏Λࣗಈબ୒ IUUQTEBHTUFSJPCMPHTPGUXBSFEFGJOFEBTTFUT