Direct translation of Haskell's Monad to Scala

Trying to learn how to program monads in Scala got some problems

Given a quick code example

import Control.Monad newtype LJ a = LJ { session :: a } instance Monad LJ where return s = LJ s (>>=) mf = f ( session m ) instance Functor LJ where fmap fm = 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 

How to write the same thing in Scala (and not a scalar)? As far as I know, it’s enough to implement the map / flatMap methods in Scala, but what comes back here? And how to bind without free variables in the instructions for ?

+4
source share
2 answers

Here is an almost direct translation, which, I believe, should answer your question. It is not completely straightforward because it does not use the types of classes that are present in the form of a template in Scala, because in the current case it will only have super simple things for no real reason.

 case class LJ[A]( session : A ) { // See it as Haskell "fmap" def map[B]( f : A => B ) : LJ[B] = LJ( f( session ) ) // See it as Haskell ">>=" 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 "do"-block def proceed2 : SimpleLJ = for { a <- auth("123", "456") b <- readFeed(a) c <- closeFeed(b) } yield c 

This solution demonstrates the classic object-oriented approach. With this approach, you cannot use the return function encapsulated in an LJ type, because you end up working at a different level, and not on the type, like using types, but in an instance of the type. Thus, the constructor of the case LJ class becomes the equivalent of return .

+9
source

I would call Nikita the answer to an idiomatic translation (which should be preferable in real situations, for example, because of understanding support), but this is definitely not the most β€œdirect” one.

 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 _) } 

Note that you can add syntactic sugar on top to get a good statement (>>=) .

+4
source

Source: https://habr.com/ru/post/980451/


All Articles