Вопрос

Scala offers a hierarchy of classes Option[T], Some[T] extends Option[T], and None extends Option[Nothing] that I have found useful for wrapping Java method calls that can return null, among other things:

val result = Option(someJavaMethodThatReturnsNull())

result acts like a sequence of zero or one items, depending on whether the Java method returned an object or null. Option has methods like map, filter, etc. that you can use just like those on a sequence and either return a new sequence (Some), if the original was Some[T], or None if the original was None.

It appears that the Clojure function seq behaves similarly: (seq x) will be a sequence of one item if x is non-null or nil if x is null. This value can then be passed to (map ...), (filter ...), etc., just like the Scala Option methods.

Am I missing something? Does this pattern make sense? Is this a "Eureka!" moment that is obvious to experienced Clojure programmers?

Это было полезно?

Решение

No. seq will either return an abstract, sequential view on a collection in case the collection is non-empty. nil otherwise. So it is completely unrelated to what you want. However you can use nil in if tests. eg.

(when-let [thing (.someMethodThatReturnsSomethingOrNil other-thing)]
  (run-only-when thing is-not-nil-or-false))

Is that what you intend?

Edit: Beware the false.

Другие советы

You always could use the maybe monad, which is close to scala Option (and haskell Maybe) without static type safety of course.

Also there is the family of ? threading functions (.?. is the better choice for java interop)

You can certainly use a Clojure sequence to represent zero, one or more return values.

It is a sensible approach and very idiomatic if your function is actually expected to return a collection.

However I would not recommend using a sequence to emulate an option type. This is likely to confuse users of your API. In Clojure, the normal and idiomatic way to is just to return nil if a value is not available, and return the value directly if it is.

Compare:

  (if-let [a (function-that-returns-nil-or-result)]
    (do-something-with a))

With what you would need if you used a sequence to emulate an option type:

  (if-let [a (function-that-returns-a-possibly-empty-sequence)]
    (do-something-with (first a)))  

I definitely prefer the first option.... why do we need or want the API user to unpack the result from the sequence with first?

In general, nil works pretty well in Clojure (much better than in other languages) because:

  • All the library functions interpret nil as an empty sequence, so you are much less likely to run into NullPointerExceptions.
  • nil is also considered "falsey" so can be conveniently used in boolean operations.

(seq x) will return nil if x is nil or an empty collection. However, map, filter etc. will accept nil as their collection argument (i.e., they won't break) and return an empty sequence. This may give the result that you expect from Scala's Option statement.

The following will each return an empty sequence:

(map (fn [x] x) (seq nil))
(map (fn [x] x) (seq `()))
(filter even? (seq nil))
(filter even? (seq `()))
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top