Monad等价于R​​uby [英] Monad equivalent in Ruby

查看:89
本文介绍了Monad等价于R​​uby的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Ruby中,monad的等价结构是什么?

>:在Ruby中,monad将是任何具有 bind 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]}]

一些实际示例



monad的一个非常简单的例子是懒惰的Identity monad,它模仿Ruby中的惰性语义(一种严格的语言):

  class Id 
def初始化(lam)
@v = lam
结束

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 是一个容器包含 40 ,但是直到第二行,通过调用 puts 语句不会输出任何内容,直到调用 force

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

不太抽象的例子就是用于从数据库中获取值的monad。假设我们有一个类 Query ,并带有一个 run(c)方法,该方法使用数据库连接 c ,以及 Query 构造函数,它们使用一个SQL字符串。因此 DatabaseValue 表示来自数据库的值。 DatabaseValue是monad:

  class DatabaseValue 
def初始化(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})}
结束

def绑定
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}))。
绑定[lambda {| id |
fromQuery(Query.new(select name from dep where id =#{id}))}]。
绑定[lambda {| name | unit [doSomethingWithDeptName(name)]}]

begin
c = openDbConnection
someResult = q.run(c)
rescue
putsError#{ $!$}
确保
c.close
结束

好的,那你为什么要这么做?因为有一些非常有用的功能可以为所有monad写 。因此,一旦你简单地实现了 unit bind ,那么你通常会反复写的代码可以重用于任何monad。例如,我们可以定义一个Monad mixin,它为所有这些类提供了一些有用的方法:

  module Monad 
I = lambda {| x | x}

#结构保留变换,它在monad环境中应用给定函数
#。
def map
lambda {| f |绑定[lambda {| x | self.class.unit [f [x]]}]}
end

#将包含另一个环境的monad环境连接到一个环境中。
def flatten
bind [I]
end

#在monad内部应用一个函数。
def ap
lambda {| x | liftM2 [I,x]}
结束

#在两个环境中绑定一个二元函数。
def liftM2
lambda {| f,m |
绑定[lambda {| x1 |
m.bind [lambda {| x2 |
self.class.unit [f [x1,x2]]
}]
}]
}
结束
结束
code>

这反过来让我们做更多有用的事情,比如定义这个函数:

 #一个内部数组迭代器[ma] => 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

序列方法需要一个在Monad中混合的类,并返回一个函数,该函数接受一个数组的monadic值,并将其转换为包含数组的monadic值。它们可以是 Id 值(将一个Identities数组转换为包含数组的Identity)或 DatabaseValue 对象返回数组的查询的数组),或函数(将函数的数组转换为返回数组的函数)或数组(将数组从内部转出)或解析器,延续,状态机,或其他任何可能混合在 Monad 模块中的东西(事实证明,它几乎适用于所有数据结构)。


What would an equivalent construct of a monad be in Ruby?

解决方案

The precise technical definition: A monad, in Ruby, would be any class with bind and self.unit methods defined such that for all instances 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]}]

Some practical examples

A very simple example of a monad is the lazy Identity monad, which emulates lazy semantics in Ruby (a strict language):

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

Using this, you can chain procs together in a lazy manner. For example, in the following, x is a container "containing" 40, but the computation is not performed until the second line, evidenced by the fact that the puts statement doesn't output anything until force is called:

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

A somewhat similar, less abstract example would be a monad for getting values out of a database. Let's presume that we have a class Query with a run(c) method that takes a database connection c, and a constructor of Query objects that takes, say, an SQL string. So DatabaseValue represents a value that's coming from the database. DatabaseValue is a monad:

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

This would let you chain database calls through a single connection, like so:

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

OK, so why on earth would you do that? Because there are extremely useful functions that can be written once for all monads. So code that you would normally write over and over can be reused for any monad once you simply implement unit and bind. For example, we can define a Monad mixin that endows all such classes with some useful methods:

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

And this in turn lets us do even more useful things, like define this function:

# 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

The sequence method takes a class that mixes in Monad, and returns a function that takes an array of monadic values and turns it into a monadic value containing an array. They could be Id values (turning an array of Identities into an Identity containing an array), or DatabaseValue objects (turning an array of queries into a query that returns an array), or functions (turning an array of functions into a function that returns an array), or arrays (turning an array of arrays inside-out), or parsers, continuations, state machines, or anything else that could possibly mix in the Monad module (which, as it turns out, is true for almost all data structures).

这篇关于Monad等价于R​​uby的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆