3

Given this Scala code:

def compute2(maybeFoo: Option[Foo]): Option[Int] =
  maybeFoo.flatMap { foo =>
    foo.bar.flatMap { bar =>
      bar.baz.map { baz =>
        baz.compute
      }
    }
  }  

Which is then translated to this for comprehension:

def compute2(maybeFoo: Option[Foo]): Option[Int] =
  for {
    foo <- maybeFoo
    bar <- foo.bar
    baz <- bar.baz
  } yield baz.compute

My question is How to convert this map/flatMap into a for comprehension in Clojure?

Assumptions:

  • If possible I'd like to use idiomatic Clojure (ie mapcat) to represent this rather than the algo.monads / fluokitten libraries. But if that is the best way to do it (I'm open to learning) then use that.
Community
  • 1
  • 1
hawkeye
  • 34,745
  • 30
  • 150
  • 304

1 Answers1

2

You probably wouldn't use Option in Clojure, but if the objects are inside collections something like this should work:

(let [maybe-foo [{:bar [{:baz [(fn [] 42)]}]}]]
  (for [foo maybe-foo
        bar (:bar foo)
        baz (:baz bar)]
    (baz)))
 ;=> '(42)

(let [maybe-foo [{:bar nil}]]
  (for [foo maybe-foo
        bar (:bar foo)
        baz (:baz bar)]
    (baz)))
;=> '()

(let [maybe-foo nil]
  (for [foo maybe-foo
        bar (:bar foo)
        baz (:baz bar)]
    (baz)))
  ;=> '()
ponzao
  • 20,684
  • 3
  • 41
  • 58
  • Ok - so if we wanted to use `Option` - then we're back to `algo.monads` http://onclojure.com/2009/03/05/a-monad-tutorial-for-clojure-programmers-part-1/ - is that correct? – hawkeye Sep 04 '14 at 05:59
  • @hawkeye, to use `for` you need to have the objects wrapped in sequences, if that is not ok then you probably need to use `algo.monads` (or roll your own). – ponzao Sep 04 '14 at 06:14
  • Ok - are you saying that a for-comprehension is basically a for loop with a let-block at the top? – hawkeye Sep 04 '14 at 10:27
  • @hawkeye, not really no. As you can see from my examples at each step it "flattens" the collection and also stops evaluation if any of the collections is empty. With `for`+`let` you would need to map over the collections manually and also flatten them. – ponzao Sep 04 '14 at 11:38
  • @hawkeye (1) I don't speak Scala, but it looks like `Option[Foo]` indicates `maybeFoo` might be an object or might be nothing. In Clojure we use `nil` for nothing, and it sequences as expected `(for [x nil] x) ;=> ())`. (2) for comprehension is like the do notation of a list monad in Haskell. – A. Webb Sep 04 '14 at 15:20