0

I am working my way through learning scalaz and Learn You A Haskell For Greater Good and wonder how to translate the filterM example from LYAHFGG to Scala.

fst $ runWriter $ filterM keepSmall [9,1,5,2,10,3]

with keepSmall defined as

keepSmall :: Int -> Writer [String] Bool  
keepSmall x  
    | x < 4 = do  
        tell ["Keeping " ++ show x]  
        return True  
    | otherwise = do  
        tell [show x ++ " is too large, throwing it away"]  
        return False

My naive approach ends with compilation errors and I have no clue how to go around that issue!

    val keepSmall: (Int => WriterT[Id, Vector[String], Boolean]) = (x: Int) => 
      if (x < 4) for {
        _ <- Vector("Keeping " + x.shows).tell
      } yield true
      else for {
        _ <- Vector(x.shows + " is too large, throwing it away").tell
      } yield false

println(List(9,1,5,2,10,3) filterM keepSmall)

Compilation errors:

 Error:(182, 32) no type parameters for method filterM: (p: Int => M[Boolean])(implicit evidence$4: scalaz.Applicative[M])M[List[Int]] exist so that it can be applied to arguments (Int => scalaz.WriterT[scalaz.Scalaz.Id,Vector[String],Boolean])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Int => scalaz.WriterT[scalaz.Scalaz.Id,Vector[String],Boolean]
 required: Int => ?M[Boolean]
    println(List(9,1,5,2,10,3) filterM keepSmall)
                               ^

and

Error:(182, 40) type mismatch;
 found   : Int => scalaz.WriterT[scalaz.Scalaz.Id,Vector[String],Boolean]
 required: Int => M[Boolean]
    println(List(9,1,5,2,10,3) filterM keepSmall)
                                       ^
mjaskowski
  • 1,479
  • 1
  • 12
  • 16

1 Answers1

2

The issue is due to the fact that Scala can't really know how to fit a type with three holes into the argument expected by filterM, which has only one hole filled with a Boolean.

You could solve your problem using some weird type-lambda syntax like this (not tested, may not work):

val keepSmall: (Int => ({type L[T] = WriterT[Id, Vector[String], T]})#L) = ...

Or (much easier) by introducing a type alias as follows:

type MyWriter[T] = WriterT[Id, Vector[String], T]
val keepSmall: (Int => MyWriter[Boolean]) = ...

This will make sure that the kind of the argument expected by filterM matches with the kind of the argument you are providing.

Aldo Stracquadanio
  • 6,167
  • 1
  • 23
  • 34
  • The second ones does the thing, thanks! The first one does not compile - tried to fix it somehow but my knowledge is too limited atm. Here is a reversed question to mine: http://stackoverflow.com/questions/8736164/what-are-type-lambdas-in-scala-and-what-are-their-benefits I guess sth similar must be available in Haskell as well but not in the particular example of Writer, right? http://stackoverflow.com/questions/4069840/lambda-for-type-expressions-in-haskell – mjaskowski Sep 21 '15 at 12:08
  • I don't know much about how Haskell deals with this but I suspect that it is a bit more flexible when partially applying types with multiple parameters. I'd like to know what is the failure for the type lambda. – Aldo Stracquadanio Sep 21 '15 at 12:53