質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

GHC

Glasgow Haskell Compiler(GHC) は Haskell コンパイラです 異なる複数のアーキテクチャのネイティブコードや、C言語へコンパイルする事ができます。

Q&A

解決済

1回答

1284閲覧

関数ファンクターにセットされる型の実体について

Meganezaru

総合スコア715

Haskell

Haskellは高い機能性をもった関数型プログラミング言語で、他の手続き型プログラミング言語では難しいとされている関数でも容易に行うことができます。強い静的型付け、遅延評価などに対応しています。

GHC

Glasgow Haskell Compiler(GHC) は Haskell コンパイラです 異なる複数のアーキテクチャのネイティブコードや、C言語へコンパイルする事ができます。

0グッド

0クリップ

投稿2019/03/08 14:14

今更ですが、Haskellを始め、未知の世界の楽しさにハマっています。

そんなところ、関数モナドが今ひとつ飲み込めず、もやもやしています。

関数モナド(Readerモナドとも呼ぶようですね)のバインドの定義と実装は、

Haskell

1 (>>=) :: m a -> (a -> m b) -> m b 2 3 h >>= f = \w -> f (h w) w

となっています。

ざっくりとした解釈では、hm a に当たる、関数ファンクターの値(単引数の関数、例えば(+3)など)がセットされると思うのですが、この場合、mには((->) r)が対応することになります。ここでのaの実体はなんなのでしょうか?

Maybeファンクターなどは、Just 3のように、aに当たるものが明確なのでわかりやすいのですが、単引数の関数から、何を読み取ればaになるのかわからないのが、もやもやの原因です。

以上、ご回答いただければ、ありがたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

一言で言うと、aは「関数の戻り値」です。

((->) r)(+ 3)のように、関数型を表す演算子->の左辺(第1型引数)にrという型変数を適用したものを表しています。
->の左辺は関数の引数の型を表していて、もう一つの引数である右辺は関数の戻り値の型を表しているのでした。
なので、例えば((->) r a)という型はr -> aと等価で、「rを受け取ってaを返す関数」を表していますし、((->) Int Bool)という型はInt -> Boolと等価で「Intを受け取ってBoolを返す関数」を表しています。

従って、((->) r)は「->に対して、引数の型rのみを適用した型」であり、それをMonadFunctorにおけるmfとして扱うと、m a((->) r a)、すなわち「rを受け取ってaを返す関数」と置き換えられることになります。
つまりaはやっぱり「戻り値の型」なんですね。

やや長くなったので一旦割愛しますが、h >>= f = \w -> f (h w) wという(>>=)の実装における各式の型を詳細に見ても、関数モナドにおけるm aaが関数の戻り値となっていることがわかるはずです。

ちなみに、((->) r a)r -> aと等価であることを確かめるには、GHCiに下記のように入力して、idなど適当な関数に対して((->) a a)という型注釈を付けてみるといいでしょう。

haskell

1> :t id :: ((->) a a) 2id :: ((->) a a) :: a -> a

さらに、敢えて間違った型注釈を付けてみると、((->) a b)という表記が関数型として認識されているのがよくわかるかと思います。

haskell

1> :t id :: ((->) a b) 2 3<interactive>:1:1: error: 4Couldn't match typea1withb15a1is a rigid type variable bound by 6 an expression type signature: 7 forall a1 b1. a1 -> b1 8 at <interactive>:1:7-16 9b1is a rigid type variable bound by 10 an expression type signature: 11 forall a1 b1. a1 -> b1 12 at <interactive>:1:7-16 13 Expected type: a1 -> b1 14 Actual type: b1 -> b1 15In the expression: id :: ((->) a b)

投稿2019/03/08 22:52

編集2019/03/08 22:54
igrep

総合スコア433

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Meganezaru

2019/03/09 01:00

丁寧なご回答、ありがとうございます! すごくわかりやすい説明で助かりました。 現時点では、もやもやがすっきりと晴れた気がしています。 (今まで、わかった!あれ違うかも・・・そういうことか!やっぱり違う・・・を繰り返してきたので、予断を許しませんが・・・笑) 今までは、形としてなんとなく理解しつつも、(+ 3)の、3(Just 3の、3と同じものに見えてしまう)に引きずられていたようです。 この場合は、3から推論されるNum型が、aの実体になるというイメージで理解はあっていますか? バインドの結果、出来上がる関数に渡される引数wの型と、m aに当たる関数の引数の型が同じにならなければならないと分かる、という感じでしょうか? 型注釈の確認も参考になります。 手段が一つ増えたので、理解が進みそうです。 とにかく、前に進めそうな雰囲気になりました! ありがとうございました!
igrep

2019/03/10 00:39

> この場合は、3から推論されるNum型が、aの実体になるというイメージで理解はあっていますか? はい、正確には、3から推論される「Num型クラスのとある型」が+の第1引数なので、戻り値と引数の型がすべて一致している、という+の型宣言から、「Num型クラスのとある型」がm aのaの実態になるということです。
Meganezaru

2019/03/10 02:27

はっきりイメージが湧きました! ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問