Skip to content

Add infix in method (element in collection) #214

@GLI-RK0

Description

@GLI-RK0

Proposal

Define a globally available infix method in(collection) for all types, enabling the following:

5 in Seq(1,5,3) // true
"cba" in "abc".permutations // true
'e' in "hello" // true
3 in (1 to 10) // true

1) Intuitive phrasing

There are many scenarios where asking whether an element is in a collection reads more intuitively than asking if a collection .contains an element.

if thing in Seq(............) then

vs

if Seq(............).contains(thing)

2) Generalizing membership checking, performantly.

Some collections kinds don't have a .contains method, like Iterator, where you would instead need to do .exists(_ == thing) to achieve the same meaning.
So, in could serve to generalize this operation -- sometimes working as an alias to .contains, and other times as an alias to .exists, while the high-level meaning is the same regardless so programmers don't need to think about this implementation detail. If you later change the underlying collection type, these callsites won't be impacted.

Implementation

It's possible to implement this for all collections performantly, through typeclasses. A few people on the discord gave a workable implementation here.

trait Membership[Item, Coll]:
  infix def in(col: Coll, item: Item): Boolean

object Membership:
  given seq: [A, S[x] <: Seq[x]] => Membership[A, S[A]]:
    override infix def in(seq: S[A], item: A): Boolean =
      seq.contains(item)
  given set: [A, S[x] <: Set[x]] => Membership[A, S[A]]:
    override infix def in(set: S[A], item: A): Boolean =
      set.contains(item)
  given iterable: [A] => Membership[A, Iterable[A]]:
    override def in(iter: Iterable[A], item: A): Boolean =
      iter.exists(_ == item)
// ...

Another example of an implementation

extension [A](a: A)
  def in[B >: A](col: Iterable[B]): Boolean =
    col match
      case seq: Seq[_] => seq.contains(a)
      case set: Set[_] => set.contains(a)
      case map: Map[_, _] => map.contains(a)
      case iter => iter.exists(_ == a)
// ...

It would be great to see this as a builtin method in the stdlib, free of any manual import requirements.

Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions