1
\$\begingroup\$

I found myself writing code of the form...

val foo : Foo = ???

val step1 : Foo = {
  if ( someCharacteristic( foo ) )
    someTransformation( foo )
  else
    foo
}
val step2 : Foo = {
  if ( otherCharacteristic( foo ) )
    otherTransformation( foo )
  else
    foo
}

val result = step2;

That seemed awkward, and it seemed like this sort of ugly stepwise processing could be monadified. I've used but not written Scala monads; I thought I'd give it a shot. What I have seems to work, but I found proving the monad laws surprisingly challenging. I'm not sure I've got a valid monad, and wonder whether there is some subtle problem that would bite me someday if I haven't. FWIW, here's the code, any comments would be very welcome.

package dumbmonad;

import scala.language.implicitConversions;

object If {
  implicit def unwrap[T]( ift : If[T] ) = ift.value;

  def evaluate[T]( ift : If[T] ) : T = if ( ift.condition( ift.value ) ) ift.transformation( ift.value ) else ift.value;

  def flatten[T]( outer : If[If[T]] ) : If[T] = evaluate( outer );

  def apply[T]( value : T ) : If[T] = apply( value, _ => false, identity );
}

case class If[T]( val value : T, val condition : (T)=>Boolean, val transformation : (T)=>T ) {

  lazy val evaluate : T = If.evaluate( this );

  def map[S]( f : (T) => S ) : If[S] = If( f( this.evaluate ) )
  def flatMap[S]( f : (T) => If[S] ) : If[S] = If.flatten( this.map( f ) )
}

I use it like this:

import dumbmonad._;
import dumbmonad.If._;

val foo : Foo = ???

for {
  a <- If( foo, someCharacteristic, someTransformation )
  b <- If( a, otherCharacteristic, otherTransformation )
} yield b;

val result : Foo = b; // implicit call to If.unwrap( ... )

Is this terrible?

\$\endgroup\$
  • \$\begingroup\$ Umm... I think you could easily solve the problem above with a Map (or even just a sequence of tuples) and two basic operations: filter and foldLeft. :) Although it doesn't really answer the question. \$\endgroup\$ – Patryk Ćwiek Nov 27 '13 at 12:50
  • \$\begingroup\$ I wanted to suggest the ternary operator (x ? y : z) instead, but I just realized it does not exist in Scala. Here is link about this topic. It is not an answer to your question, but related. \$\endgroup\$ – toto2 Nov 27 '13 at 23:26
4
\$\begingroup\$

What you are describing are simply functions.

To take a related example from mathematics,

          /   x, if x >= 0
abs(x) =  |
          \  -x, otherwise

And you are just composing functions: f(g(x)).

You can somewhat make the correspondence to monads, but it is a bit of a stretch. I think it is more that monads are extensions of functions rather than the other way around.

flatMap would be the composition operator: o , ie (f o g)(x) = f(g(x)). And unit would just be the identity operation: I(x) = x.

The three monad laws:

  • Associativity: f o (g o h) = (f o g) o h
  • Left unit: I o f = f
  • Right unit: f o I = f

So instead of defining your If class, you can just stick to def, the usual function definition. Functional programming!!! In your case you can compose all your functions in any order since they always input/output Foo type. You can compose functions in Scala with val h = f _ compose h _, or using andThen.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.