4

尝试学习如何在 Scala 中编写 monad,遇到了一些麻烦

鉴于快速代码示例

import Control.Monad

newtype LJ a = LJ { session :: a }

instance Monad LJ where
  return s = LJ s
  (>>=) m f = f ( session m )

instance Functor LJ where
  fmap f m = LJ . f $ session m

type SimpleLJ = LJ String

auth :: String -> String -> SimpleLJ
auth = undefined

readFeed :: String -> SimpleLJ
readFeed = undefined

closeFeed :: String -> SimpleLJ
closeFeed = undefined

proceed = auth "123" "456" >>= readFeed >>= closeFeed

我如何在 Scala(不是 scalaz)中写同样的东西?据我所知,在 scala 中实现 map/flatMap 方法就足够了,但是这里的 return 是什么?以及如何在for语句中没有自由变量的情况下进行绑定?

4

2 回答 2

9

这是一个几乎直接的翻译,我相信它应该回答你的问题。它不是完全直接的,因为它不使用在 Scala 中以模式形式存在的类型类,因为在当前情况下,它只会在没有真正原因的情况下过于复杂。

case class LJ[A]( session : A ) {
  // See it as Haskell's "fmap"
  def map[B]( f : A => B ) : LJ[B] = 
    LJ( f( session ) )
  // See it as Haskell's ">>="
  def flatMap[B]( f : A => LJ[B] ) : LJ[B] =
    f( session )
}

type SimpleLJ = LJ[String]

def auth( a : String, b : String ) : SimpleLJ = ???

def readFeed( a : String ) : SimpleLJ = ???

def closeFeed( a : String ) : SimpleLJ = ???

def proceed : SimpleLJ = 
  auth("123", "456").flatMap(readFeed).flatMap(closeFeed)

// Same as above but using a for-comprehension, which is 
// used as a replacement for Haskell's "do"-block
def proceed2 : SimpleLJ = 
  for {
    a <- auth("123", "456")
    b <- readFeed(a)
    c <- closeFeed(b)
  } 
  yield c

该解决方案演示了一种经典的面向对象方法。使用这种方法,您不能将return函数封装在LJ类型中,因为您最终会在另一个级别上工作 - 不是像类型类那样在类型上工作,而是在类型的实例上工作。所以LJcase 类的构造函数就变成了return.

于 2013-03-01T06:58:55.997 回答
4

我会认为 Nikita 的答案是惯用的翻译(在现实世界的情况下应该首选,例如因为理解支持),但它绝对不是最“直接”的翻译。

class LJ[A](val session : A)

trait Functor[F[_]] {
   def fmap[A,B](fa:F[A])(f:A => B) : F[B]
}

trait Monad[M[_]] {
   def pure[A](a:A):M[A]
   def bind[A,B](ma:M[A])(f:A => M[B]):M[B]
}

object LJFunctor extends Functor[LJ] {
  def fmap[A,B](lj:LJ[A])(f:A => B) = new LJ(f(lj.session))
}

object LJMonad extends Monad[LJ] {
   def pure[A](a:A) = new LJ(a)
   def bind[A,B](lj:LJ[A])(f:A => LJ[B]) = f(lj.session)
}


object MonadTest {

  type SimpleLJ = LJ[String]

  def auth(s:String, t:String):SimpleLJ = null

  def readFeed(s:String):SimpleLJ = null

  def closeFeed(s:String):SimpleLJ = null

  val proceed = LJMonad.bind(LJMonad.bind(auth("123","456"))(readFeed _))(closeFeed _)
}

请注意,您可以在顶部添加一些语法糖以获得良好的(>>=)运算符。

于 2013-03-01T14:47:20.510 回答