scala / docs.scala-lang

The Scala Documentation website
http://docs.scala-lang.org
563 stars 1.03k forks source link

we do not have a method with name tailOption in the scala collections #2338

Open robatipoor opened 2 years ago

robatipoor commented 2 years ago

Written in this part of the scala3 book collections-methods

tail throws an java.lang.UnsupportedOperationException if the list is empty, so just like head and headOption, there’s also a tailOption method, which is preferred in functional programming.

But we do not have a method with name tailOption in the scala collections

julienrf commented 2 years ago

Hello, good catch! Would you be interested in contributing a fix?

mukesh210 commented 2 years ago

@julienrf Is this issue still relevant?? I see we have "last" and "lastOption" already defined which works just like "tail" and "tailOption".

bishabosha commented 2 years ago

@julienrf Is this issue still relevant?? I see we have "last" and "lastOption" already defined which works just like "tail" and "tailOption".

tailOption would return Option[Collection[T]] because it is the remaining collection with only the first element removed, or None for an empty collection. lastOption returns Option[T] because it is the last element of the collection, (which could be the first element for a singleton collection) or None for an empty collection

Ichoran commented 2 years ago

The book should be fixed. The more convenient way, almost always, to do "tailOption" is to do drop(1). It doesn't cover every possible use case, but it covers enough so that the clutter of two more methods (tailOption and initOption) isn't worth it.

The few odd cases where you actually need tailOption should be handled with pipe instead, especially if you're using Scala 3 where you can define a fully efficient one:

extension [A](a: A)
  inline def pipe[B](inline f: A => B): B = f(a)

Any time you have an urge to write

someBigThingYouDontWantToRepeat.tailOption.map{ tail =>
  foo(tail)
}

you can choose one of the following excellent alternatives, and with the power of pipe you have many more options to do non-tailOptiony things, too.

someBigThingYouDontWantToRepeat.pipe{ xs =>
  if xs.isEmpty then None
  else Some(foo(xs.tail))
}

someBigThingYouDontWantToRepeat.pipe{ xs =>
  if xs.isEmpty then someDefault
  else foo(xs.tail)
}

someBigThingYouDontWantToRepeat.pipe{ xs =>
  xs.headOption{ head =>
    foo(head, xs.tail)
  }
}

someBigThingYouDontWantToRepeate.match{
  case head +: tail => foo(tail)
  case _ => someDefault
}