Option equality

3,082 views
Skip to first unread message

Nils Kilden-Pedersen

unread,
May 25, 2013, 8:28:04 PM5/25/13
to scala-user
Is there a succinct way to compare equality between two Options, but which returns false in the case of None?

Nils Kilden-Pedersen

unread,
May 25, 2013, 8:29:59 PM5/25/13
to scala-user
And I mean better than this:

opt1.isDefined && opt1 == opt2

which is succinct, but not FP "pretty" :-)

Vlad Patryshev

unread,
May 25, 2013, 9:49:17 PM5/25/13
to Nils Kilden-Pedersen, scala-user
This is weird; you will have x !=x, and the hell will rise. 

Thanks,
-Vlad


On Sat, May 25, 2013 at 5:28 PM, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
Is there a succinct way to compare equality between two Options, but which returns false in the case of None?

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Todd O'Bryan

unread,
May 25, 2013, 10:24:18 PM5/25/13
to Nils Kilden-Pedersen, scala-user
(opt1, opt2) match {
case (Some(x1), Some(x2)) => x1 == x2
case _ => false
}
works, but is not very succinct.

opt1.flatMap(x1 => opt2.map(_ == x1)).getOrElse(false)

works and is succinct, but I wrote it and I'm not even sure what it does.

I think I like yours better, assuming that opt1 == opt2 is the same as
opt1.get == opt2.get if they're both Some[X] and false if they're not.
Weirdly enough, I can't figure out where the equals/== method is
defined for Option.

Also, as I was playing with this question, I discovered that the
Scaladoc and the behavior for 2.10.1 don't agree for several methods
in Option. For example, the ++ method says that it returns an
Option[B].

But:

scala> val opt1 = Some(3)
opt1: Some[Int] = Some(3)

scala> val opt2 = Some(4)
opt2: Some[Int] = Some(4)

scala> opt1 ++ opt2
res0: Iterable[Int] = List(3, 4)

And List(3, 4) is not an Option[Int], no matter what you try to do to
it. (At least, I don't think it is.)

Todd

Ryan Tanner

unread,
May 25, 2013, 11:03:34 PM5/25/13
to scala...@googlegroups.com
You might restructure your code to use a for comprehension instead.

for {
    x1 <- Some(1)
    x2 <- Some(1)
    if x1 == x2
} yield { ... }

This will return Some of whatever is returned within the yield code block.  You'll end up with a None if either x1 != x2 or either is a None..

Kevin Scaldeferri

unread,
May 25, 2013, 11:19:09 PM5/25/13
to Nils Kilden-Pedersen, scala-user
opt1.map(_ => opt1 == opt2).getOrElse(false)

is "functional" and reasonably concise, although I'm not sure I'd actually prefer it over using isDefined.


Todd O'Bryan

unread,
May 26, 2013, 12:05:20 AM5/26/13
to Ryan Tanner, scala-user
Actually, try this:

for {
x1 <- Some(1)
x2 <- None
if x1 == x2
} yield true

In 2.10.1's console, I get:

<console>:11: error: ambiguous reference to overloaded definition,
both method == in class Int of type (x: Char)Boolean
and method == in class Int of type (x: Short)Boolean
match argument types (Nothing) and expected result type Boolean
if x1 == x2
^
which is fairly annoying.

Jason Zaugg

unread,
May 26, 2013, 2:41:01 AM5/26/13
to Todd O'Bryan, Nils Kilden-Pedersen, scala-user
On Sun, May 26, 2013 at 4:24 AM, Todd O'Bryan <toddo...@gmail.com> wrote:
I think I like yours better, assuming that opt1 == opt2 is the same as
opt1.get == opt2.get if they're both Some[X] and false if they're not.
Weirdly enough, I can't figure out where the equals/== method is
defined for Option.

Some is a case class; None is a case object. They have synthetic structural equality and hashing.

-jason

Seth Tisue

unread,
May 26, 2013, 9:32:09 AM5/26/13
to scala-user
On Sat, May 25, 2013 at 7:29 PM, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
And I mean better than this:
opt1.isDefined && opt1 == opt2
which is succinct, but not FP "pretty" :-)

My best effort is
PartialFunction.cond((opt1, opt2)){case (Some(x), Some(y)) => x == y} 

Miles Sabin

unread,
May 26, 2013, 9:49:32 AM5/26/13
to Nils Kilden-Pedersen, scala-user
On Sun, May 26, 2013 at 1:29 AM, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
> And I mean better than this:
>
> opt1.isDefined && opt1 == opt2
>
> which is succinct, but not FP "pretty" :-)

The tersest "cute" solution I can come up with is,

opt1.toRight(false) == opt2.toRight(true)

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
skype: milessabin
gtalk: mi...@milessabin.com
g+: http://www.milessabin.com
http://twitter.com/milessabin

Stefan Hoeck

unread,
May 26, 2013, 9:54:03 AM5/26/13
to scala...@googlegroups.com
Here's another one:

  import scalaz._, Scalaz._

  ^(o1, o2){ _ == _ } | false

Cheers, Stefan

Stefan Hoeck

unread,
May 26, 2013, 9:58:41 AM5/26/13
to scala...@googlegroups.com
Even better (type safe and one character less):
 
  import scalaz._, Scalaz._

  ^(o1, o2){ _ ≟ _ } | false

Cheers, Stefan

Seth Tisue

unread,
May 26, 2013, 9:59:03 AM5/26/13
to scala...@googlegroups.com
A rival golfer offers:
opt1.exists(_ => opt1 == opt2)

but let's shave off a few more strokes:
opt1.exists(Some(_) == opt2)

(though I'll admit that in a no-holds-barred, to-the-death golf match, the former wins again with single-character variable names and no whitespace)

chelck

unread,
May 26, 2013, 10:06:10 AM5/26/13
to scala...@googlegroups.com
I can't help but ask "What's wrong with your original code?" It is clear and to the point. Wrap it in a function and who cares how it is implemented. Are the words "pretty" and "cryptic" being mixed up? What is not functional about the original? 

-Chris

Stefan Hoeck

unread,
May 26, 2013, 10:57:39 AM5/26/13
to scala...@googlegroups.com
Challenge accepted, dear golfer. Can you beat 13 characters? (a and b are Options; scalaz imports omitted - in any reasonable .scala file they are there anyway):

a?(a≟b)|false

Somebody call this cryptic?

Cheers, Stefan

Stefan Hoeck

unread,
May 26, 2013, 11:24:43 AM5/26/13
to scala...@googlegroups.com
9 characters:

a∃(_⇒a≟b)

although I must admit that this is very close to Seth's solution; :-(

Nils Kilden-Pedersen

unread,
May 26, 2013, 1:39:10 PM5/26/13
to chelck, scala...@googlegroups.com
On Sun, May 26, 2013 at 9:06 AM, chelck <christop...@gmail.com> wrote:
I can't help but ask "What's wrong with your original code?" It is clear and to the point. Wrap it in a function and who cares how it is implemented. Are the words "pretty" and "cryptic" being mixed up? What is not functional about the original? 

I just couldn't help but feel that this was an already known concept in FP and perhaps there was a idiomatic approach to this.

I think the one that comes closest, by being both succinct, readable, and doesn't feel hacky, is Kevin's proposal:

opt1.map(_ => opt1 == opt2).getOrElse(false)

But again, it's hard to see that it's any substantial improvement.

I was hoping for something like op1 === opt2, or some other succinct operator.
 

-Chris


On Saturday, May 25, 2013 8:29:59 PM UTC-4, Nils wrote:
And I mean better than this:

opt1.isDefined && opt1 == opt2

which is succinct, but not FP "pretty" :-)

On Sat, May 25, 2013 at 7:28 PM, Nils Kilden-Pedersen <nil...@gmail.com> wrote:
Is there a succinct way to compare equality between two Options, but which returns false in the case of None?

Miles Sabin

unread,
May 26, 2013, 7:05:54 PM5/26/13
to Nils Kilden-Pedersen, chelck, scala-user
On Sun, May 26, 2013 at 6:39 PM, Nils Kilden-Pedersen
<ni...@kilden-pedersen.net> wrote:
> I think the one that comes closest, by being both succinct, readable, and
> doesn't feel hacky, is Kevin's proposal:
>
> opt1.map(_ => opt1 == opt2).getOrElse(false)
>
> But again, it's hard to see that it's any substantial improvement.
>
> I was hoping for something like op1 === opt2, or some other succinct
> operator.

From an aesthetic point of view I think that both Kevin's solution and
your original are lacking in that they mask the symmetry between opt1
and opt2 ... and I suspect it's that symmetry that makes your
hypothetical op1 === opt2 seem more attractive. Both Stefan's solution
and mine are nicely symmetric (which I guess is really what I meant by
"cute" ;-)

Nils Kilden-Pedersen

unread,
May 26, 2013, 7:24:22 PM5/26/13
to Miles Sabin, Nils Kilden-Pedersen, chelck, scala-user
On Sun, May 26, 2013 at 6:05 PM, Miles Sabin <mi...@milessabin.com> wrote:
On Sun, May 26, 2013 at 6:39 PM, Nils Kilden-Pedersen
<ni...@kilden-pedersen.net> wrote:
> I think the one that comes closest, by being both succinct, readable, and
> doesn't feel hacky, is Kevin's proposal:
>
> opt1.map(_ => opt1 == opt2).getOrElse(false)
>
> But again, it's hard to see that it's any substantial improvement.
>
> I was hoping for something like op1 === opt2, or some other succinct
> operator.

From an aesthetic point of view I think that both Kevin's solution and
your original are lacking in that they mask the symmetry between opt1
and opt2 ... and I suspect it's that symmetry that makes your
hypothetical op1 === opt2 seem more attractive.

Probably.

For some reason, in my mind, it feels like the difference between exists and forall.

 
Both Stefan's solution
and mine are nicely symmetric (which I guess is really what I meant by
"cute" ;-)

Yes, but yours is also more "noisy" by involving Either (Right to be exact), and Stefan's is just unreadable to me, and probably anyone else who isn't intimate with scalaz.

Anyway, it's always fun to see what clever things come up.
 

Cheers,


Miles

--
Miles Sabin
tel: +44 7813 944 528
skype: milessabin
gtalk: mi...@milessabin.com
g+: http://www.milessabin.com
http://twitter.com/milessabin

Stefan Hoeck

unread,
May 26, 2013, 11:30:50 PM5/26/13
to scala...@googlegroups.com, Miles Sabin, Nils Kilden-Pedersen, chelck

Yes, but yours is also more "noisy" by involving Either (Right to be exact), and Stefan's is just unreadable to me, and probably anyone else who isn't intimate with scalaz.

Which of course does not prevent you from learning something from these solutions. Mine (the first and second one; the others were just a bit of fun) make use of scalaz's syntax for Applicative Functors - a very powerful concept in FP. It's a bit like using for-comprehensions for Monads: At first it is very unfamiliar but once you get used to it, it's actually highly readable code.

Cheers, Stefan
 

Miles Sabin

unread,
May 27, 2013, 1:49:48 AM5/27/13
to Stefan Hoeck, chelck, Nils Kilden-Pedersen, scala...@googlegroups.com


On 27 May 2013 04:30, "Stefan Hoeck" <efasc...@gmail.com> wrote:
> Which of course does not prevent you from learning something from these solutions.

Agreed. Mine illustrates how projecting values into a richer type allows finer discriminations to be made: observe how after the .toRights both the LHS and RHS inhabit Either[Int, Boolean] in a way which trivializes the equivalence you're looking for.

Cheers,

Miles

Reply all
Reply to author
Forward
0 new messages