39

Rubyではモナドの同等の構成は何でしょうか?

4

4 に答える 4

75

bind正確な技術的定義: Ruby のモナドは、self.unitすべてのインスタンスに対して m:

m.class.unit[a].bind[f] == f[a]
m.bind[m.class.unit] == m  
m.bind[f].bind[g] == m.bind[lambda {|x| f[x].bind[g]}]

いくつかの実用的な例

モナドの非常に単純な例は、Ruby (厳密な言語) で遅延セマンティクスをエミュレートする遅延アイデンティティ モナドです。

class Id
  def initialize(lam)
    @v = lam
  end

  def force
    @v[]
  end

  def self.unit
    lambda {|x| Id.new(lambda { x })}
  end

  def bind
    x = self
    lambda {|f| Id.new(lambda { f[x.force] })}
  end
end

これを使用すると、遅延した方法で proc を連鎖させることができます。たとえば、次xのコンテナは "containing"ですが、が呼び出されるまでステートメントが何も出力しない40という事実からもわかるように、計算は 2 行目まで実行されません。putsforce

x = Id.new(lambda {20}).bind[lambda {|x| puts x; Id.unit[x * 2]}]
x.force

少し似ているが抽象度の低い例は、データベースから値を取得するためのモナドです。データベース接続を受け取るメソッドを持つクラスQueryと、たとえば SQL 文字列を受け取るオブジェクトのコンストラクターがあると仮定しましょう。Soは、データベースからの値を表します。DatabaseValue はモナドです:run(c)cQueryDatabaseValue

class DatabaseValue
  def initialize(lam)
    @cont = lam
  end

  def self.fromQuery(q)
    DatabaseValue.new(lambda {|c| q.run(c) })
  end

  def run(c)
    @cont[c]
  end

  def self.unit
    lambda {|x| DatabaseValue.new(lambda {|c| x })}
  end

  def bind
    x = self
    lambda {|f| DatabaseValue.new(lambda {|c| f[x.run(c)].run(c) })}
  end
end

これにより、次のように、単一の接続を介してデータベース呼び出しを連鎖させることができます。

q = unit["John"].bind[lambda {|n|
  fromQuery(Query.new("select dep_id from emp where name = #{n}")).
    bind[lambda {|id|
      fromQuery(Query.new("select name from dep where id = #{id}"))}].
        bind[lambda { |name| unit[doSomethingWithDeptName(name)] }]

begin
  c = openDbConnection
  someResult = q.run(c)
rescue
  puts "Error #{$!}"
ensure
  c.close
end

では、一体なぜそんなことをするのでしょうか? すべてのモナドに対して一度に書くことができる非常に便利な関数があるからです。そのため、通常なら何度も書き直すコードは、 and を実装するだけで、任意のモナドに再利用できunitますbind。たとえば、そのようなすべてのクラスにいくつかの便利なメソッドを与える Monad mixin を定義できます。

module Monad
  I = lambda {|x| x }

  # Structure-preserving transform that applies the given function
  # across the monad environment.
  def map
    lambda {|f| bind[lambda {|x| self.class.unit[f[x]] }]}
  end

  # Joins a monad environment containing another into one environment.
  def flatten
    bind[I]
  end

  # Applies a function internally in the monad.
  def ap
    lambda {|x| liftM2[I,x] }
  end

  # Binds a binary function across two environments.
  def liftM2
    lambda {|f, m|
      bind[lambda {|x1|
        m.bind[lambda {|x2|
          self.class.unit[f[x1,x2]]
        }]
      }]
    }
  end
end

これにより、次の関数を定義するなど、さらに便利なことを行うことができます。

# An internal array iterator [m a] => m [a]
def sequence(m)
  snoc = lambda {|xs, x| xs + [x]}
  lambda {|ms| ms.inject(m.unit[[]], &(lambda {|x, xs| x.liftM2[snoc, xs] }))}
end

このsequenceメソッドは、Monad に混在するクラスを受け取り、モナド値の配列を取り、それを配列を含むモナド値に変換する関数を返します。それらは、Id値 (ID の配列を配列を含む ID に変換する)、DatabaseValueオブジェクト (クエリの配列を配列を返すクエリに変換する)、または関数 (関数の配列を配列を返す関数に変換する) である可能性があります。 )、または配列(配列の配列を裏返しにする)、またはパーサー、継続、ステートマシン、またはMonadモジュールに混在する可能性のあるその他のもの(結局のところ、ほとんどすべてのデータ構造に当てはまります)。

于 2010-04-25T18:53:03.730 に答える
6

2 セントを追加すると、hzap はモナドの概念を誤解していると言えます。それは単に「型インターフェイス」や「特定の機能を提供する構造」であるだけでなく、それ以上のものです。これは、Ken と Apocalisp が言ったように、厳密な規則に従う操作 (bind (>>=) と unit (return)) を提供する抽象的な構造です。

モナドに興味があり、これらの回答で述べられているいくつかのことよりも詳しく知りたい場合は、次を読むことを強くお勧めします: Monads for function programming (pdf), by Wadler.

じゃあ!

PS: 私はあなたの質問に直接答えていないようですが、Apocalisp は既に回答しています。

于 2010-04-25T19:26:50.667 に答える
5

モナドは言語構造ではありません。それらは特定のインターフェースを実装する型に過ぎず、Ruby は動的に型付けされるためcollect、配列、結合メソッド ( flatten1 つのレベルのみを平坦化するなど)、および何でもラップできるコンストラクターなどを実装するクラスはすべてモナドです。 .

于 2010-04-25T18:35:59.777 に答える
1

上記の回答に従ってください:

Ruby用のモナドミックスインを実装するRubygemであるRumonadeをチェックすることに興味があるかもしれません。

Romandeはミックスインとして実装されているため、ホストクラスがメソッドself.unit#bind(およびオプションでself.empty)を実装することを期待し、残りは機能するようにします。

Scalaで慣れているように、これを使ってmapやり直すことができます。また、ScalazのValidationクラスである検証からいくつかの優れた複数の失敗の戻り値を取得することもできます。Option

于 2012-05-02T21:58:25.750 に答える