蒙纳德用简单的英语?(适用于无FP背景的OOP程序员)

蒙纳德用简单的英语?(适用于无FP背景的OOP程序员),oop,functional-programming,monads,Oop,Functional Programming,Monads,对于OOP程序员来说(没有任何函数式编程背景),什么是单子? 它解决了什么问题?最常用的地方是什么 更新 为了澄清我所寻求的理解,假设您正在将一个包含单子的FP应用程序转换为OOP应用程序。您将如何将Monad的职责转移到OOP应用程序?来源: 在函数式编程中,单子是 一种抽象数据类型,用于 表示计算(而不是 域模型中的数据)。单子 允许程序员链接操作 共同建造一条管道,其中 每一个动作都装饰有 提供了其他处理规则 由单子。用计算机编写的程序 功能性风格可以利用 monad来构造 包括顺序操作[

对于OOP程序员来说(没有任何函数式编程背景),什么是单子?

它解决了什么问题?最常用的地方是什么

更新 为了澄清我所寻求的理解,假设您正在将一个包含单子的FP应用程序转换为OOP应用程序。您将如何将Monad的职责转移到OOP应用程序?

来源:

在函数式编程中,单子是 一种抽象数据类型,用于 表示计算(而不是 域模型中的数据)。单子 允许程序员链接操作 共同建造一条管道,其中 每一个动作都装饰有 提供了其他处理规则 由单子。用计算机编写的程序 功能性风格可以利用 monad来构造 包括顺序操作[2] 或定义任意控制流 (比如处理并发, 继续,或例外)

形式上,monad是由 定义两个操作(绑定和 return)和类型构造函数M 必须满足几个属性才能 允许正确组成 一元函数(即 使用monad中的值作为它们的 参数)。返回操作需要一段时间 来自普通类型的值,并将其放入 放入M型的一元容器中。 绑定操作执行以下操作: 反向处理,提取 来自容器的原始值和 将其传递给关联的下一个 管道中的功能

程序员将编写一元代码 用于定义数据处理过程的函数 管道单子充当一个角色 框架,因为它是一种可重用的行为 这决定了 特定的一元函数 调用管道,并管理所有 警方要求的卧底工作 计算[3]绑定和返回 在管道中交错的运算符 将在每个一元之后执行 函数返回控件,并将 注意特定的方面 由单子处理


我相信它解释得很好。

更新:这个问题是一个非常长的博客系列的主题,你可以在上阅读-谢谢你提出了这个伟大的问题

在OOP程序员理解的术语中(没有任何函数式编程背景),什么是monad

单子是一种类型的“放大器”,它遵循一定的规则并提供一定的运算

首先,什么是“类型放大器”?我指的是一些系统,它可以让你把一种类型转换成一种更特殊的类型。例如,在C++中考虑<代码>可空< /代码>。这是一个类型的放大器。它允许您获取一个类型,比如说
int
,并为该类型添加一个新功能,即现在它可以为null,而以前它不能

作为第二个例子,请考虑<代码> iQueaby。这是一个类型的放大器。它允许您获取一个类型,例如,

string
,并为该类型添加一个新功能,即您现在可以从任意数量的单个字符串中生成一个字符串序列

什么是“特定规则”?简单地说,对于底层类型上的函数来说,有一种合理的方法来处理放大类型,从而使它们遵循函数组合的正常规则。例如,如果你有一个关于整数的函数,比如

intm(intx){返回x+N(x*2);}
然后,
Nullable
上的相应函数可以使其中的所有运算符和调用“以与以前相同的方式”一起工作

(这是难以置信的模糊和不精确;你要求的解释没有假设任何关于功能组合的知识。)

什么是“行动”

  • 有一个“单位”操作(有时令人困惑地称为“返回”操作),它从普通类型中获取一个值并创建等效的一元值。从本质上讲,这提供了一种方法,可以获取未放大类型的值,并将其转换为放大类型的值。它可以在OO语言中实现为构造函数

  • 有一个“绑定”操作,它接受一个一元值和一个可以转换该值的函数,并返回一个新的一元值。绑定是定义monad语义的关键操作。它允许我们将未放大类型上的操作转换为放大类型上的操作,这符合前面提到的函数组合规则

  • 通常有一种方法可以从放大类型中恢复未放大类型。严格地说,这个操作不需要有monad。(如果你想有一个共同点,我们不会再考虑这篇文章中的内容。) 同样,以
    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