やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

【感想・まとめ】「関数型プログラミングなんもわからん。を考えよう」に参加してきました。

関数型プログラミングなんもわからん。を考えよう」とは🤔

connpass.com

ABAB↑↓BA (@ababupdownba) | Twitterさんが主催された関数型プログラミングの疑問や質問について、ひたすら有識者の方々が丁寧に回答をしてくれる・ディスカッションする会です。わかる枠わからない枠の2枠が用意されておりまして、初心者の方であっても気軽に参加することが出来ます。

ただ、質問して積極的に参加した方がより楽しめると思いますので、初心者であっても聞きたいことはいくつか用意しておくと良さそうです。 「お、面白そう!」と思いノリと勢いを元に、今回はわからない枠で参加させて頂きました。

主催者のABAB↑↓BAさん
twitter.com

勉強会はDiscordにて開催されて、カメラONは任意のボイスチャット主体で進行され、質問はFigmaに付箋として貼られていくものにひたすら回答するというスタイルでした。初対面の方が多い中で中々、話し出すのに勇気が要りますが、Figmaに質問を貼っておけばテーマとして扱っていただけたので聞きたい事が聞けて帰ってくる事ができました。

以下、個人的に気になった事と自分が質問した内容について簡単に自分の言葉でまとめてみました。他の質問や、参加者の方が書いてくださった丁寧な回答のメモが以下のFigmaの共有リンクから確認可能ですので、気になった方は覗いてみてください。

www.figma.com

気になった事 / 質問したこと🧐

関数をどうまとめればいいですかね…? オブジェクト指向はオブジェクトの責務でまとめたり…

難しい質問で場合による。
- オブジェクト指向との親和性はある
  - 型やデータ構造でまとめるのが基本
- 関数型だとかオブジェクト指向とかパラダイムによらない

この話を聞いて思い出しのはgolangの構造体に対して関数を定義した実装です。定義したデータ構造に対して、処理を作っていくというのがオブジェクト指向だろうが、関数型だろうが、手続き型であっても、ベストプラクティスになるのだと感じました。

type Pizza struct {
    Size int;
    Price int;
}

func (p Pizza) getSize() int {
    return p.Size
}

func (p *Pizza) setSize(newSize int) *Pizza {
    p.Size = newSize
    return p
}

play.golang.org

関数型らしい書き方ってどんな感じになるでしょうか…? オブジェクト指向っぽい書き方になってしまう…。

- 大切なのは読みやすいコードを書くこと
- haskellには制約が多い。変数はイミュータブルのみ。副作用は除外されている  -> haskellっぽい書き方
- 言語の文化に合わせることが重要。関数型らしさよりも気にした方が良い
- 理解のために言語のコアのパッケージがどう作られているのか見てみるのが良い

このテーマは自分もかなり気になっていました。自分が最初に触れた関数型言語elixirです。書籍プログラミングElixirでは以下のような内容を習得しました。

  • ループ構文がないため再帰関数を書くこと
  • map, filter, reduce等を使ってリスト主体のデータを処理するパイプラインを組み上げること
  • 無名関数、高階関数のパワフルさ

しかしhaskellなどに代表されるような、強力な型システムと型推論、純粋な関数と副作用の分離、イミュータブル(変更不可能)な変数については部分的に登場するものの、詳細は語られていません。そのためhaskellのような純粋関数型言語に謎の憧れがあったりました。haskellに近い状態になる書き方が正しいという先入観がありましたが、elixirにはelixirの文化があるのだということを改めて認識し、いっそう好きになりました。

とはいえ純粋な処理と副作用のある処理の分離、可能な限りイミュータブルな変数を使わないというのはどの言語でも共通して目指す所なのではないかなと感じました。

関数型の言語にはモナドとかの概念が出てくると思うんですけど、そこで圏論とかの数学的な知見の学習って必須だったりしますか?

- 必須ではない。なくても大丈夫
- 数学が出来なくても関数型言語を記述することは可能
- 圏論の知識を持つことで抽象的な理解をすることが出来る -> 他言語を触ったときに「あぁ、これね」と理解がしやすくなる

確かに。haskellというと圏論圏論というと数学...という非常に難しいイメージが先行しております。自分も過去にモナドについて理解しようと数学の定義を覗いて「おっ」となった事があります。しかし数学は数学であり、プログラミングはプログラミングとのことで、数学の定義が必ずしも値やルールの定義ではないので、知らないからコードが書けないわけではないそうです。

自分が現在読み進めている入門Haskellプログラミングでも「習得には書くのが一番だよ〜」と記述されていました。その通りですね。

関数型のパラダイムを学ぶことで得られる強みはなんでしょうか?

質問させて頂きまして、ご教示を頂きました🙇‍♂️

- 関数型言語だからバグが減ったような感じはしない
- バグが減らせている印象がある。
  - たとえばif分岐だとしても、Elm、HaskellだとElseも必ず書かないといけない
  - 全てのパターンを網羅する必要がある。 -> Null・Undefinedエラーにならない
- 例外的な扱いが型で扱えるので、失敗パターンを処理する強制力がバグを減らす効果があるように感じる
- 制約が強い言語であればあるほど、書き方が統一される
- テストが後付けで絶対に書ける(IOは難しい) -> 全ての関数が値を返す
- 型があることで変更に強くなるコードが書ける

たくさん回答を頂きました。変更に強くテストが書きやすいというのはバグを減らすという視点とはまた別の利点でした。忘れてしまいがちなnullのケースを必ず捌く必要があるというのも記述は大変になる分、やはり非常に強力です。現在、本番稼働で発生しているエラーのほとんどがnullによるエラーによるものです。このエラーがなくなれば、どれほど安心か...

まとめ📖

様々なテーマが扱われておりました。
今の自分には理解が難しいテーマもありましたが、関数型というテーマだけでこれだけ話が広がるというのは素晴らしい事だと感じました。合わせて、同じよう悩みを多くの方が抱えているということにも改めて気づくことが出来ました。モチベーションが上がりました。素敵な会をありがとうございました💪

connpass.com

www.figma.com

おまけ: 関数型に期待していること

現在、とあるプロダクトにてスクラムマスターという立場でチームメンバーが記述したコードをレビューする事が多くあります。その中で、変更される変数やファイル操作のような副作用が各所に見られており、実際にこれらがバグの現在になることが非常に多いです。またバグの発生時には上記の状態のコードは原因特定が非常に難しいのです。

いかにして関数型のパラダイムを取り込んで、バグの少ないコードを書き、プロダクトの品質を高く保っていくかという事に非常に期待を持っております。元々は興味本位で初めたelixirですが、今日の話を通して、自分の実現したい事がハッキリしました。