What would an equivalent construct of a monad be in Ruby?
问题:
回答1:
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).
回答2:
To add my two cents, I'd say that hzap has misunderstood the concept of monads. It's not only a « type interface » or a « structure providing some specific functions », it's muck more than that. It's an abstract structure providing operations (bind (>>=) and unit (return)) which follow, as Ken and Apocalisp said, strict rules.
If you're interested by monads and want to know more about them than the few things said in these answers, I strongly advise you to read : Monads for functional programming (pdf), by Wadler.
See ya!
PS: I see I don't directly answer your question, but Apocalisp already did, and I think (at least hope) that my precisions were worth it
回答3:
Monads are not language constructs. They're just types that implement a particular interface, and since Ruby is dynamically typed, any class that implement something like collect
in arrays, a join method (like flatten
but only flattens one level), and a constructor that can wrap anything, is a monad.
回答4:
Following on the above answers:
You may be interested in checking out Rumonade, a ruby gem which implements a Monad mix-in for Ruby.
Romande is implemented as mix-in, so it expect its host class to implement the methods self.unit
and #bind
(and optionally, self.empty
), and will do the rest to make things work for you.
You can use it to map
over Option
, as you are used to in Scala, and you can even get some nice multiple-failure return values from validations, a la Scalaz's Validation class.