Scala style andThen

Hello,

Is the following code optimal in sense of Scala idiomatic style? Is it ok that “case None” appears first? Is it possible to rewrite the logic using “andThen” operator?

  def isStraightFlush(cards: CardList): Option[Rank] = {
    isStraight(cards) match {
      case None => None
      case _ => isFlush(cards) match {
        case None => None
        case _ => Some(Rank(Rank.STRAIGHT_FLUSH, cards, cards))
      }
    }
  }

Thank you.

def isStraightFlush(cards: CardList): Option[Rank] = for {
  _ <- isStraight(cards)
  _ <- isFlush(cards)
  } yield Rank(Rank.STRAIGHT_FLUSH, cards, cards)

Seems a lot simpler

2 Likes

It seems isStrait and isFlush could both return Boolean.

if (isStraight(cards) && isFlush(cards))
  Some(Rank(Rank.STRAIGHT_FLUSH, cards, cards))
else
  None

Or you could turn Option into Boolean

if (isStraight(cards).isDefined && isFlush(cards).isDefined)
  Some(Rank(Rank.STRAIGHT_FLUSH, cards, cards))
else
  None

Option as a sequence. This is the perfect solution! Thank you!

That’s not really “option as a sequence” - Option has its own “native” notion of #map/#flatMap().

1 Like

Hello,

  • Having the None case first looks fine

  • Method names that start with “is” are usually used for methods that return Boolean

  • The logic looks incorrect to me: if we see “flush” simply as a synonym for “all cards of the same suit” and “straight” as a synonym for “cards form a sequence”, then we could say that “straight flush” is the intersection of “flush” and “straight”. However, when we start talking about ranks, that is not really true: “straight flush”, “flush” and “straight” are three different ranks that do not intersect, so it is incorrect to say that the rank “straight flush” is the intersection of the rank “flush” and the rank “straight”.

Thank you for the note about prefix “is”. I’ve renamed these methods to probe* .
I’m sure the logic is correct. The “straight flush” is combination of “straight” + “flush”. Yes, they are three different ranks. My code for rank evaluation from 5 cards set looks like:

def evalCards(funcs: List[CardList => Option[Rank]], cards: CardList): Option[Rank] = {
  funcs match {
    case List() => None
    case hd :: tail => hd(cards) match {
      case None => evalCards(tail, cards)
      case x => x
    }
  }
}

val allCombs: List[CardList => Option[Rank]] = List(
  probeStraightFlush,
  probeFourOfKind,
  probeFullHouse,
  probeFlush,
  probeStraight,
  probeThreeOfKind,
  probeTwoPairs,
  probePair,
  probeHighCard)

evalCards(allCombs, cards)