尝试学习如何编写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方法,但这里返回的是什么?如何在 for 语句中没有自由变量进行绑定?
答案 0 :(得分: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
类型中,因为您最终在另一个级别上工作 - 不是在类型类型的类型上,而是在类型的实例上。因此LJ
案例类构造函数成为return
的对应部分。
答案 1 :(得分: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 _)
}
请注意,您可以在顶部添加一些语法糖,以便获得一个不错的(>>=)
运算符。