蒙纳德用简单的英语?(适用于无FP背景的OOP程序员)
对于OOP程序员来说(没有任何函数式编程背景),什么是单子? 它解决了什么问题?最常用的地方是什么 更新 为了澄清我所寻求的理解,假设您正在将一个包含单子的FP应用程序转换为OOP应用程序。您将如何将Monad的职责转移到OOP应用程序?来源: 在函数式编程中,单子是 一种抽象数据类型,用于 表示计算(而不是 域模型中的数据)。单子 允许程序员链接操作 共同建造一条管道,其中 每一个动作都装饰有 提供了其他处理规则 由单子。用计算机编写的程序 功能性风格可以利用 monad来构造 包括顺序操作[2] 或定义任意控制流 (比如处理并发, 继续,或例外) 形式上,monad是由 定义两个操作(绑定和 return)和类型构造函数M 必须满足几个属性才能 允许正确组成 一元函数(即 使用monad中的值作为它们的 参数)。返回操作需要一段时间 来自普通类型的值,并将其放入 放入M型的一元容器中。 绑定操作执行以下操作: 反向处理,提取 来自容器的原始值和 将其传递给关联的下一个 管道中的功能 程序员将编写一元代码 用于定义数据处理过程的函数 管道单子充当一个角色 框架,因为它是一种可重用的行为 这决定了 特定的一元函数 调用管道,并管理所有 警方要求的卧底工作 计算[3]绑定和返回 在管道中交错的运算符 将在每个一元之后执行 函数返回控件,并将 注意特定的方面 由单子处理蒙纳德用简单的英语?(适用于无FP背景的OOP程序员),oop,functional-programming,monads,Oop,Functional Programming,Monads,对于OOP程序员来说(没有任何函数式编程背景),什么是单子? 它解决了什么问题?最常用的地方是什么 更新 为了澄清我所寻求的理解,假设您正在将一个包含单子的FP应用程序转换为OOP应用程序。您将如何将Monad的职责转移到OOP应用程序?来源: 在函数式编程中,单子是 一种抽象数据类型,用于 表示计算(而不是 域模型中的数据)。单子 允许程序员链接操作 共同建造一条管道,其中 每一个动作都装饰有 提供了其他处理规则 由单子。用计算机编写的程序 功能性风格可以利用 monad来构造 包括顺序操作[
我相信它解释得很好。更新:这个问题是一个非常长的博客系列的主题,你可以在上阅读-谢谢你提出了这个伟大的问题 在OOP程序员理解的术语中(没有任何函数式编程背景),什么是monad 单子是一种类型的“放大器”,它遵循一定的规则并提供一定的运算 首先,什么是“类型放大器”?我指的是一些系统,它可以让你把一种类型转换成一种更特殊的类型。例如,在C++中考虑<代码>可空< /代码>。这是一个类型的放大器。它允许您获取一个类型,比如说
int
,并为该类型添加一个新功能,即现在它可以为null,而以前它不能
作为第二个例子,请考虑<代码> iQueaby。这是一个类型的放大器。它允许您获取一个类型,例如,
string
,并为该类型添加一个新功能,即您现在可以从任意数量的单个字符串中生成一个字符串序列
什么是“特定规则”?简单地说,对于底层类型上的函数来说,有一种合理的方法来处理放大类型,从而使它们遵循函数组合的正常规则。例如,如果你有一个关于整数的函数,比如
intm(intx){返回x+N(x*2);}
然后,Nullable
上的相应函数可以使其中的所有运算符和调用“以与以前相同的方式”一起工作
(这是难以置信的模糊和不精确;你要求的解释没有假设任何关于功能组合的知识。)
什么是“行动”
Nullable
为例。您可以使用构造函数将int
转换为Nullable
。C#编译器会为您处理大多数可为null的“提升”,但如果没有,提升转换就很简单:比如说
intm(intx){whatever}
变成
Nullable M(Nullable x)
{
如果(x==null)
返回null;
其他的
返回新的可空值(无论什么);
}
将Nullable
转换回int
是通过属性完成的
关键是函数转换。注意可空操作的实际语义是如何在转换中捕获的,即null
上的操作传播null
。我们可以概括这一点
假设您有一个从int
到int
的函数,就像我们原来的M
一样。您可以很容易地将其转换为一个函数,该函数接受int
并返回Nullable
,因为您只需运行
# the return function
def unit[A] (x: A): M[A]
# called "bind" in Haskell
def flatMap[A,B] (m: M[A]) (f: A => M[B]): M[B]
# Other two can be written in term of the first two:
def map[A,B] (m: M[A]) (f: A => B): M[B] =
flatMap(m){ x => unit(f(x)) }
def andThen[A,B] (ma: M[A]) (mb: M[B]): M[B] =
flatMap(ma){ x => mb }
def unit[A] (x: A): Option[A] = Some(x)
def flatMap[A,B](m:Option[A])(f:A =>Option[B]): Option[B] =
m match {
case None => None
case Some(x) => f(x)
}
def unit[A] (x: A): List[A] = List(x)
def flatMap[A,B](m:List[A])(f:A =>List[B]): List[B] =
m match {
case Nil => Nil
case x::xs => f(x) ::: flatMap(xs)(f)
}
for {
i <- 1 to 4
j <- 1 to i
k <- 1 to j
} yield i*j*k
(1 to 4).flatMap { i =>
(1 to i).flatMap { j =>
(1 to j).map { k =>
i*j*k }}}
lookupVenue: String => Option[Venue]
getLoggedInUser: SessionID => Option[User]
reserveTable: (Venue, User) => Option[ConfNo]
val user = getLoggedInUser(session)
val confirm =
if(!user.isDefined) None
else lookupVenue(name) match {
case None => None
case Some(venue) =>
val confno = reserveTable(venue, user.get)
if(confno.isDefined)
mailTo(confno.get, user.get)
confno
}
val confirm = for {
venue <- lookupVenue(name)
user <- getLoggedInUser(session)
confno <- reserveTable(venue, user)
} yield {
mailTo(confno, user)
confno
}
instance MonadState s (State s) where
put = ...
get = ...
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
m >>= f >>= g
do x <- m
y <- f x
g y
(>>) :: m a -> m b -> m b
m = do x <- someQuery
someAction x
theNextAction
andSoOn
join :: m (m a) -> m a
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
data RandomF r a = GetRandom (r -> a) deriving Functor
type Random r a = Free (RandomF r) a
type RandomT m a = Random (m a) (m a) -- model randomness in a monad by computing random monad elements.
getRandom :: Random r r
runRandomIO :: Random r a -> IO a (use some kind of IO-based backend to run)
runRandomIO' :: Random r a -> IO a (use some other kind of IO-based backend)
runRandomList :: Random r a -> [a] (some kind of list-based backend (for pseudo-randoms))
class CMonadic<T> {
static CMonadic<T> create(T t); // a.k.a., "return" in Haskell
public CMonadic<U> flatMap<U>(Func<T, CMonadic<U>> f); // a.k.a. "bind" in Haskell
}
CMonadic<T>.create(t).flatMap(f) == f(t)
instance.flatMap(CMonadic<T>.create) == instance
instance.flatMap(f).flatMap(g) == instance.flatMap(t => f(t).flatMap(g))
List<int>.create(1) --> [1]
intList.flatMap(x => List<int>.makeFromTwoItems(x, x*10)) --> [1,10,2,20,3,30]
intList.flatMap(x => List<int>.makeFromTwo(x, x*10))
.flatMap(x => x % 3 == 0
? List<string>.create("x = " + x.toString())
: List<string>.empty())
interface IMonad<T> {
static IMonad<T> create(T t); // not allowed
public IMonad<U> flatMap<U>(Func<T, IMonad<U>> f); // not specific enough,
// because the function must return the same kind of monad, not just any monad
}
-- a * b
multiply :: Int -> Int -> Maybe Int
multiply a b = return (a*b)
-- divideBy 5 100 = 100 / 5
divideBy :: Int -> Int -> Maybe Int
divideBy 0 _ = Nothing -- dividing by 0 gives NOTHING
divideBy denom num = return (quot num denom) -- quotient of num / denom
-- tagged value
val1 = Just 160
-- array of functions feeded with val1
array1 = val1 >>= divideBy 2 >>= multiply 3 >>= divideBy 4 >>= multiply 3
-- array of funcionts created with the do notation
-- equals array1 but for the feeded val1
array2 :: Int -> Maybe Int
array2 n = do
v <- divideBy 2 n
v <- multiply 3 v
v <- divideBy 4 v
v <- multiply 3 v
return v
-- array of functions,
-- the first >>= performs 160 / 0, returning Nothing
-- the second >>= has to perform Nothing >>= multiply 3 ....
-- and simply returns Nothing without calling multiply 3 ....
array3 = val1 >>= divideBy 0 >>= multiply 3 >>= divideBy 4 >>= multiply 3
main = do
print array1
print (array2 160)
print array3
type MyMonad = [Int -> Maybe Int] -- my monad as a real array of functions
myArray1 = [divideBy 2, multiply 3, divideBy 4, multiply 3]
-- function for the machinery of executing each function i with the result provided by function i-1
runMyMonad :: Maybe Int -> MyMonad -> Maybe Int
runMyMonad val [] = val
runMyMonad Nothing _ = Nothing
runMyMonad (Just val) (f:fs) = runMyMonad (f val) fs
print (runMyMonad (Just 160) myArray1)
#include <iostream>
#include <string>
template <class A> class M
{
public:
A val;
std::string messages;
};
template <class A, class B>
M<B> feed(M<B> (*f)(A), M<A> x)
{
M<B> m = f(x.val);
m.messages = x.messages + m.messages;
return m;
}
template <class A>
M<A> wrap(A x)
{
M<A> m;
m.val = x;
m.messages = "";
return m;
}
class T {};
class U {};
class V {};
M<U> g(V x)
{
M<U> m;
m.messages = "called g.\n";
return m;
}
M<T> f(U x)
{
M<T> m;
m.messages = "called f.\n";
return m;
}
int main()
{
V x;
M<T> m = feed(f, feed(g, wrap(x)));
std::cout << m.messages;
}
f: x -> 1 / x
g: y -> 2 * y
e = {
type: error,
message: 'I got error trying to divide 1 by 0'
}
e = {
type: error,
message: 'Our committee to decide what is 1/0 is currently away'
}
1/0 // => Infinity
parseInt(Infinity) // => NaN
NaN < 0 // => false
false + 1 // => 1
Something<Integer> i = new Something("a")
.flatMap(doOneThing)
.flatMap(doAnother)
.flatMap(toInt)
A => C = A => B andThen B => C
A => F[C] = A => F[B] bind B => F[C]
A => F[B] = A => B andThen return