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

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

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

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

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

2722閲覧

モナド等を理解したい

SolitaryWalker

総合スコア20

Haskell

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

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2019/03/21 13:11

Haskellのファンクタ・アプリカティブ・モナドについて記述されたコードを見ていますが、いまいちピンと来ていません。
そこで、Haskellのコードを普段使い慣れているC#で書き直してみたのですが、合っているかどうかもわかりません。
以下にHaskellのコードと、それに対応すると思って作成したC#のコードを上げます。
合っているかどうか確認したいです。もし間違っている場合はコードを修正するか、全て書き直して教えてもらえると助かります。
宜しくお願いします。


ファンクタについて:
Haskell版

class Functor f where fmap::(a -> b) -> f a -> f b

C#版

interface Hoge<Type> {/*適当な実装*/} interface IFunctor { Hoge<B> Fmap<A, B>(Func<A, B> func, Hoge<A> hoge); }

アプリカティブについて:
Haskell版

class Applicative f where (<*>):: f (a -> b) -> f a -> f b

C#版

interface Hoge<Type> {/*適当な実装*/} interface IApplicative { Hoge<B> Applicative<A, B>(Hoge<Func<A, B>> func, Hoge<A> hoge); }

モナドについて:
Haskell版

class Monad m where (>>=):: m a -> (a -> m b) -> m b

C#版

interface Hoge<Type> {/*適当な実装*/} interface IMonad { Hoge<B> Monad<A, B>(Hoge<A> hoge, Func<A, Hoge<B>> func); }

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

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

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

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

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

guest

回答2

0

ベストアンサー

残念ながら C# の型システムには高カインド多相(高階多相)が備わっていないため表現することができません。

ですので、C# に似た架空の言語で実装してみます。(Kotlin や Scala からも連想しています。)

型クラスに相当するのがトレートです。

lang

1trait Functor<F<_>> 2{ 3 F<S> Map<T, S>(Func<T, S> f, F<T> a); 4} 5 6trait Applicative<F<_>> : Functor<F<_>> 7{ 8 F<S> App<T, S>(F<Func<T, S>> f, F<T> a); 9 F<T> Pure<T>(T a); 10} 11 12trait Monad<F<_>> : Applicative<F<_>> 13{ 14 F<S> Bind<T, S>(F<T> a, Func<T, F<S>> f); 15}

下記のような Maybe 型を考えてみましょう。

lang

1abstract class Maybe<T> 2{ 3 public sealed class Just (T value) : Maybe<T> {} 4 5 public sealed class Nothing : Maybe<T> {} 6}

これを先のトレートのインスタンスにしていきます。

lang

1instance Functor<Maybe<_>> 2{ 3 Maybe<S> Map<T, S>(Func<T, S> f, Maybe<T> ma) => 4 ma switch { 5 Maybe<T>.Just (var a) => new Maybe<S>.Just(f(a)), 6 Maybe<T>.Nothing => new Maybe<S>.Nothing() 7 }; 8} 9 10instance Applicative<Maybe<_>> 11{ 12 Maybe<S> App<T, S>(Maybe<Func<T, S>> mf, Maybe<T> ma) => 13 (mf, ma) switch { 14 (Maybe<Func<T, S>>.Just (var f), Maybe<T>.Just (var a)) => new Maybe<S>.Just(f(a)), 15 (Maybe<Func<T, S>>.Nothing, _) => new Maybe<S>.Nothing(), 16 (_, Maybe<T>.Nothing) => new Maybe<S>.Nothing() 17 }; 18 19 Maybe<T> Pure<T>(T a) => new Maybe<T>.Just(a); 20} 21 22instance Monad<Maybe<_>> 23{ 24 Maybe<S> Bind<T, S>(Maybe<T> ma, Func<T, Maybe<S>> f) => 25 ma switch { 26 Maybe<T>.Just (var a) => f(a), 27 Maybe<T>.Nothing => Maybe<S>.Nothing 28 }; 29}

使用する場合はこのようなコードを想定しています。

lang

1/// 名前から関数を探す関数 2/// 見付からなければ Nothing 3Maybe<Func<int, int>> FindFunction(string name) { … } 4 5/// ユーザーの入力が整数ならその値 6/// そうでなければ Nothing 7Maybe<int> InputInt() { … } 8 9Monad<Maybe<_>>.Bind<Func<int, int>, string>( 10 FindFunction("minus"), 11 f => // FindFunction で見付かった関数が代入されるのが f 12 // f の型は Func<int, int> 13 Monad<Maybe<_>>.Bind<int, string>( 14 InputInt(), 15 i => // ユーザーの入力した整数値が代入されるのが i 16 Applicative<Maybe<_>>.Pure<string>(i.ToString()) 17 ) 18); 19// 全体の値は、 20// minus 関数が見付からなければ Nothing 21// ユーザーの入力が整数値でなければ Nothing 22// それ以外ならば、ユーザーの入力が 10 とすると "-10" という文字列が Just に包まれたもの 23 24// 型推論がバリバリ効いて型引数を書かなくていいなら下記のように書けます 25Monad.Bind( 26 FindFunction("minus"), 27 f => 28 Monad.Bind( 29 InputInt(), 30 i => Applicative.Pure(i.ToString()) 31 ) 32);

投稿2019/03/21 18:08

編集2020/11/01 00:35
kakkun61

総合スコア285

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

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

SolitaryWalker

2019/03/22 01:23 編集

回答有り難うございます。 オブジェクト指向と関数型で比較するという事自体がそもそもなのかもしれませんね;; コードを見ていて気になった点があります。 ```.kts trait Monad<F<_>> : Applicative<F<_>> { Maybe<S> Bind<T, S>(F<T> a, Func<T, F<S>> f); } ``` というのは、 ```.kts trait Monad<F<_>> : Applicative<F<_>> { F<S> Bind<T, S>(F<T> a, Func<T, F<S>> f); } ``` ……ではないんですか? また、 ```.kts instance Functor<Maybe<_>> { Maybe<S> Map<T, S>(Func<T, S> f, Maybe<T> ma) => a switch { Maybe<T>.Just (var a) => new Maybe<S>.Just(f(a)), Maybe<T>.Nothing => new Maybe<S>.Nothing() }; } ``` で用いられている引数maはどのように使われているのでしょうか?
SolitaryWalker

2019/03/22 01:34 編集

回答ありがとうございました! 初めてお答えいただいた時間が深夜ということで、非常に疲れている中で回答いただいたと思っております。 重ね重ねではありますが、非常に感謝しております。 本当にありがとうございました!
guest

0

@kakkun61 さんの回答の通り、C#では言語仕様上MonadやApplicativeといった概念を直接表現することはできません。
言及されている「高カインド多相」というのは、簡単に言うとListやMaybeなどのような、「型パラメーターをとる型」を抽象化する機能のことです。
ListやMaybe, そして@SolitaryWalkerさんが例として挙げたHoge<Type>型はそのような「型パラメーターをとる型」になってますよね?(例ではinterfaceとなってますが、「/*適当な実装*/」と仰ってることから読み替えてます)

ただ、雰囲気としてはだいたい合ってます。
MonadやApplicative, Functorを(やや強引ながらも)C#で例えるなら、interfaceで表現するのが適切です。
付け加えると、下記の問題があります。

  • Applicative, Monad それぞれに必要な、purereturnといったメソッド(もちろんこの場合の「メソッド」は型クラスの「メソッド」を指しますよ!)が再現できていません。
    恐らく、C#ではコンストラクターについての制約はinterfaceで表現できないために敢えてそうしているんだと思います。
    完全な解決ではありませんが、対応するBuilderの為のインターフェースを作るのがよいかとおもいます。(詳細は後で紹介する私の記事をご覧ください)
  • <*>>>=といった記号の関数は、それぞれapply, bind等と発音されることが多いので、ApplicativeメソッドやMonadメソッドではなく、Applyメソッド、Bindメソッドと名付けた方が適切かと思います。

余談、かつ手前味噌で恐縮ですが同じようなことにJavaで取り組んだ記事があります。

igreque : Info -> JavaでMonadをはじめからていねいに

この記事でもC#と同様の問題により完全にJavaでMonadを再現することはできていないのですが、参考までにどうぞ。

また、この記事でも触れてるんですが、どうにかJavaにMonadやFunctorを再現したhighjというライブラリーがあります。
こちらは、「高カインド多相」という機能をすごくトリッキーな方法でシミュレートしています。
例えばこちらにFunctor, こちらにMonadの再現があるので興味があれば探検してみてください。

投稿2019/03/21 23:25

igrep

総合スコア428

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

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

SolitaryWalker

2019/03/22 01:26

回答ありがとうございました! サイトも拝見いたしました。 今回は先にお答え頂いたkakkun61さんの回答をベストアンサーとさせていただきますが、 igrepさんの回答も非常に参考になりました! また機会があれば宜しくお願いします!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問