hiruberuto
@hiruberuto

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

関数型言語を学ぶときに難しいと感じた部分はどこですか?

関数型プログラミング言語は学ぶのが難しい、使うのが難しい、という声がしばしば聞かれます。一方で、関数型言語にはシンプルでよく整理された言語仕様を持つものも多く、他の言語より学びにくいとは必ずしも言えないと思います。関数型言語が難しいと言われる理由を知りたいので、あなたが関数型言語を学んだときに難しいと感じた部分、使うのが難しいと感じた部分は何だったのかを教えてください。

なお、『関数型言語』の定義については深入りしないことにします。その言語自身が関数型言語を標榜していたり、回答者がそれを関数型言語だと認識している言語であれば結構です。関数型を含むパルチパラダイム言語については、そのうち関数型言語としての部分になるべく絞ってお答えください。関数型プログラミングのスタイルを実現するとされるライブラリを学ぶときの困難さについては、今回は対象外とします。

回答のフォーマットは自由ですが、次のような項目について教えていただければ参考になります。

  • 学んでみた関数型言語は何ですか?
  • それ以前に学んだことのある言語にはどんなものがありますか?
  • 他の言語に比べてどこが難しいと感じましたか?

回答例

たとえば、質問者 @hiruberuto の場合は次のような回答になります。

  • 学んでみた関数型言語: Haskell, PureScript, Elm
  • それ以前に触ったことのある言語: Java, C#, Visual Basicなど
  • 難しいと感じたところ: 型システムと遅延評価。Haskellのような言語のユーザのあいだにはより厳密な型を使おうとする文化があるためか、関数の型やデータ型が他の言語に比べて難しくなっているように思います。型エラーも読み解くのが難しいです。また、Haskellではデフォルト遅延評価のために、デバッガで1ステップずつ計算の過程を追うことができなかったのもつらかったです。学び始めた当時はHaskellの日本語の資料が少なかったのも苦労しました。
15

10Answer

  • 学んでみた関数型言語: Haskell, OCaml, Elixir
  • それ以前に触ったことのある言語: Ruby, C, Javaなど
  • 難しいと感じたところ: 初めはオブジェクト指向プログラミングでは必須のステートを伴うような実装を関数型言語でどのように行うのかでものすごいつまづいた記憶があります!特にリストに対しての再帰とパターンマッチを用いたテクニックや畳み込みなどをうまく書けずに四苦八苦していました。型推論も手続き型言語とはやはり勝手が微妙に異なるので、初めはとても抵抗がありました。
    あとはやはりモナドでしょうか。初めモナドの定義式を読んだ時意味がわからず途方にくれた記憶があります笑

全体的に僕の関数型言語のつまづきポイントは、手続き型言語で培った知識を活用して関数型言語を理解しようとしてしまっていたところに尽きる気がします。

10Like

個人的にもこのあたりはいろいろ考えるところですが、ひとまずはシンプルなポイントを3つほど書いてみたいと思います。

  • 学んでみた関数型言語は何ですか?:Haskell, Elm
  • それ以前に学んだことのある言語にはどんなものがありますか?:BASIC, アセンブラ(Z80, MC68kなど)、C、D、FORTRAN、Java、JavaScript、PHP、Perl、Python、Matlab、他
  • 他の言語に比べてどこが難しいと感じましたか?
  1. 他の言語で当たり前のようにできる「場当たり的な実装」が難しい

例えば「ちょっとログる」とか「グローバル変数をちょこちょこっと」とか。
場当たり的なことが手軽にできない、というのは、品質確保に結びつくことが多いですが、
「つらいなー、めんどいなー」とついつい思うこともあります。

  1. ネットの内容がコピペができないことが多い

そのままコピペでいけることもありますけど、「型をちゃんとあわせないといけない」ことも
多い印象ですね。「コピペするにしても、ちゃんと理解してから使え」ということにもなり、
これまた品質確保に結びつきますけどね。

  1. 制御構造の制約が強すぎ、と誤解してしまう

手続き型言語でいう「break」をそのまま表せる文法がないため、思った制御構造を実装するには
ある程度のスキルと慣れがいりますね。

6Like

学んでみた関数型言語: Elm
それ以前に触ったことのある言語: JavaScript, PHP, Ruby
難しいと感じたところ: 関数の中から関数の外に影響を及ぼせない。戻り値でしか外界に伝えられない。それどころか変数に再代入すらできない。

でも、全体的にはそんなに難しく感じませんでした。
むしろ入門しやすいなぁ、と感じました。

関数型について学んだら、関数型じゃない言語に戻ったときにも使える知見が増えた感じがします。(当社比)

5Like
  • 学んでみた関数型言語: Scala, Elm
  • それ以前に触ったことのある言語: C++, Java
  • 難しいと感じたところ:
    • 副作用を必要とする処理を書くとき。当時、ScalaFXを使ってUIを作っているときに、イミュータブルにこだわりすぎててハマった記憶があります。関数型言語で副作用が発生する処理を行う場合、多くの場合は言語やフレームワーク側でミュータブルな部分を隠蔽していますが、それすらも許容してははならない、と義務的に感じていて、ハマっていました。
    • コピーを返して値を更新する、という考え方。C++でゲーム作っていたときはリソースを使わないことが正義で、「コピー=悪」だったので、そもそもコピーを作るというところに意識が向かず、ハマることが多かったです。
5Like
  • 学んでみた関数型言語は何ですか?
    • Scala, Haskell, Elixir, Erlang, Elm
    • ちらっとだけOCaml, F#
  • それ以前に学んだことのある言語にはどんなものがありますか?
    • Java, Ruby, JavaScript, PHP, ShellScript
  • 他の言語に比べてどこが難しいと感じましたか?
    • (大抵の関数型言語で)すべてが「式」であり、0個以上の値を受け取って、1個の値を返す「関数」でプログラムが構成されるところ
      • 関数は必ず1個の値を返すのだから、それをどうするのか?(基本的に3択で、変数に束縛して他で使う、別の関数に渡す、現在の関数スコープの最後の式であればその関数の返り値とする)を避けて通ることが許されない
        • Elixirなど、言語によっては上記3択を外れた場合暗黙的に返り値は捨てられるという機能もある
      • 翻って、この「式の返り値」以外になにかが裏で起こるならそれはすべて副作用なのだが、個人的にはこれらが腹落ちしたとき関数型言語がわかったと感じた
5Like

駄長文ポエムになりますが……。

学んでみたものと触ったことのあるもの

  • 学んでみた関数型言語: Haskell, Elm, PureScript, Lisp(Emacs), Scheme(GNU Guile), LINQ(チェーンメソッドスタイル)
  • それ以前に触ったことのある言語: VisualBasic(dotnet含む), CSharp, Python, PHP, BashShell, PowerShell, JavaScript(Node.js含む)

難しいと感じたところ

学習開始当初

真っ先に思い出せるのは、オブジェクト指向、手続きや構造化の考え方でガッチガチでしたので、
「ゑ? 変数を使わないプログラミングって一体……」
というところでしょうか。

その後LINQを使いたくてちょっと関数型パラダイム(というよりラムダ式・ラムダ計算)の学習をしましたが当時21歳専門学校卒直後で就業の私には、
「ゑ? なんで関数に関数を重ねると数値になるの? さらにそれが真偽判定になぜ使えるの?」
という感覚を覚えた記憶があります。

その後の経過

時は流れて、テスト駆動開発とバージョン管理システム導入の説得ができないことを会社のせいにして転職・無職・派遣社員登録となって6年経った頃ですかね、JavaScriptがECMAの2015年規格ができてという頃でしたでしょうか。その時の派遣先でNode.jsのスクリプトを組むに当たってJavaScriptの Promise.all() が使えない事には思い通りの逐次実行ができないことを体感して勉強して上記もろもろが何故か紐解けました。復習と再学習の機会だったのかもしれません。

さらに転機のようにUnity Engineでのゲーム開発の現場に派遣された際にPowerShellでのスクリプトを組んでいくうちに、
「あ。(動的型付けだけど)関数型パラダイムってこういうパイプライン的な処理を書く時にとてつもなく便利だな」
という感覚を覚え、ついでにその当時プロジェクトで導入していたのもあって「リアクティブプログラミング」を学んだり、ソースコードを読んで、
「はえー。LINQ(厳密には違うとは思いますが)のチェーンメソッドってこうやって使えるのか」
と漠然と理解しながらそれぞれのメソッドの処理について色々調べたり、暇を見つけたら他の関数型パラダイムの言語のチュートリアルだけでも触って理解しようという意欲はありました。そのおかげでdotNetになってしまいましたがイベントの動き方、副作用とは……が把握できたかと。

その後も紆余曲折あり、利用するエディタをAtomからSpacemacs, DOOM Emacsに移りLISPの魅力に取り憑かれてしまったので「Land of Lisp」を買ってScheme(GNU Guile)をちょっとさわったり。といったところです。


まだ語り足りないのですが、このへんで。

おまけ: 逆に今、むしろこのままで良いのかと感じていること 「全ての関数はラムダ式っぽいのでは?」 という理解です。  チャーチ数とかなんだとか考えた時に、 「数値を意識してしまうから関数の重ね合わせが理解できないのかもしれないようなきがしないでもない」 という、自分の発想ながらにも暴論なので、この思い違いが他人に関数型パラダイムを伝える時に足かせになるのではと思ったりします。  また、自分の場合は関数型パラダイムがチョトワカルと思い込み初めた辺りから数理論理学の方に没入してきてしまってるので、なおのこと自分の感じたまま伝えると混乱を招いてしまいそうと感じております。
3Like
  • 学んでみた関数型言語:Common Lisp
  • それ以前に学んだことのある言語:JavaScript
  • 難しいと感じたところ:
    • 最初はHaskellを学ぼうとしていたのですが、関数型と型システムの両方の難しさから挫折し、関数型の要素のみに触れるためLispを学び始めました。
    • 基本的な高階関数であるmapやfilterなどから学び始めましたが、reduceのような再帰的な動きをする関数を理解するまで時間がかかりました。高階関数自体に関しては、処理を渡すという発想が自然に思えたため特に難しく感じることはありませんでした。
    • 再帰的な関数を書いたあと、それを末尾再帰の形に書き換えるのが難しいと感じました。これについては何度も書いて感覚を掴むしかないと感じました。

ある程度関数型の言語に慣れたあと、強い型システムをもつ言語としてPureScriptに触れましたが、こちらは入出力というとても基本的な操作からモナドが必要になるため、モナドに関する知識の理解に時間がかかりました。

3Like

C++、Ruby、(Java|Type)Script、Scala、Haskell、Rust(の関数型の部分)を使ってみての感想です。以前学んだことのある、というか現在進行形で関数型も非関数型も使っているので、それらの比較になります。

まず基本的に、「関数型特有の困難」というのはそのまま数学的な理論の難解さでしょうね。私は特に苦労していませんが、数学に慣れていない人は難しいと思います。

「関数型に特有ではない困難」で言えば、「静的型付け」と「immutability」のそれぞれに起因するものがあります。アロー関数やラムダ式の型が分からない、というのは何度も経験しています。Haskell のような、もともと関数型として作られた言語であれば良いのですが、C++ や TypeScript、Rust だと、今でも型が分からなくて混乱することはあります。

まあラムダ式の型はググれば見つかるのでまだ良しとしましょう。ですが immutability は、多くの場合プログラムの設計を一から見直す必要があるので大変です。関数型とは無関係に immutability の話になりますが、jQuery(mutable)から React(immutable)へ移行するのには苦労しました。jQuery で書いたコードのほぼすべてが使えなくなり、まるで新しいアプリを一から作るような気分でした。

あとは「関数型言語はググっても情報が少ない」という問題もありますね。Ruby や JavaScript、Rust といった「なんちゃって関数型言語」を差し置いてまで関数型言語に傾倒するメリットはまったく無いので、わざわざ使う人もいないのでしょう。でもそれは「自分もパイオニアになれる」ということでもあるので、前向きに捉えることにします。

3Like
  • 学んでみた関数型言語:Haskell, PureScript
  • それ以前に触ったことのある言語:C++, Java, Ruby, JavaScript
  • 難しいと感じたところ:全体的に難しく感じました、、、けど特に「持ち上げる(リフト)」のイメージがうまくつかめなかったことです。自分は変数の数を少なくして、同じ変数を使い回すクセがあったので、都度新変数名用意しなくてはいけないなど、基本的なことでハマりました。あと、Haskellは、文字列型がいろいろあるのと、ライブラリのバージョンアンマッチ、PureScriptはエラーの内容からエラー内容がよく分からず、、。
2Like

学んでみた関数型言語: Haskell
それ以前に触ったことのある言語: PHP
難しいと感じたところ:
・環境構築
・カリー化

すごいH本を見ながらやりました。
とにかく環境構築が難しかった覚えがあります。
ここで脱落する方が多いと思います。
カリー化は当時は何が嬉しいのかよくわかりませんでした。

あとなぜPHPが主流なんだろう、という疑問に苛まれました。

2Like

Your answer might help someone💌