Skip to end of metadata
Go to start of metadata

Pattern matching allows us to check if an object has some particular structure. For example, that a node of a binary tree has both children, or that it has a left child that has no children, and so on.

Pattern matching operators (is and !is)

Pattern matching is performed by is operator that returns true if the pattern matched successfully and false otherwise. It has a negated form !is. The simplest form of pattern matching simply checks if an object conforms to a given type (similar to Java's instanceof check):

//is 
if (obj is String) { 
  print(obj.length) 
} 
// !is 
if (obj !is String) { 
  print("Not a String") 
} 
else { 
  print(obj.length) 
}

See smart casts.

Is and !is may be used as branch conditions in when expressions:

when (x) { 
  is Int -> print(x) 
  is List<*> -> print(x.size()) 
  !is Number -> print("Not even a number") 
  else -> print("can't do anything") 
}
When-expressions can replace switch-statement without pattern matching
See more on when expressions here.

Patterns

Patterns on the right-hand side of is and !is allow to match against types, constants, structure of tuples and other objects, and to bind objects matched by sub-patterns, to variables.

See the grammar for patterns.

Complex patterns are deferred for future versions
For now you can only use a type after is/!is. See the corresponding issue.

What's next

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jul 20, 2011

    Why in Tree example we use class name in matching:

    when(xml) { is Tree @ (*,null) =>

    but for Date example we use decomposer method name:

    when(d) { is mmddyy @ (05,13,val a)=>

    Is it because Tree constructor is used as decomposer function? Or this is the name of Extension function defined later as decomposer fun Any?.Tree()

    1. Jul 20, 2011

      It is an extension function Any?.Tree()

  2. Jul 21, 2011

    Anonymous

    Can regular functions (not declared as "decomposer") be used with "@"? What about free-form values of function types?

    1. Jul 21, 2011

      Yes, they can

      Actually, it can be an expression there, it is resolved in the context of when's subject, and must be of a tuple type

  3. Jul 21, 2011

    Anonymous

    What is the scope of variables bound by a match? It's clear in case of "when/is", but not so much in "if" statements - are their bodies treated specially somehow? Does this also work for short-circuiting operators like "a && b", such that I can bind a val in a pattern match inside "a", and then use that binding inside "b"? Any other special cases?

    1. Jul 21, 2011

      You can say

      if (a != y && a is Foo @ (val b)) { 
        b.foo() // b is visible here 
      }

      But not much more than that, because the scopes become too difficult to track for a human being.

      1. Jul 23, 2011

        "Too difficult to track for a human being".  Code smell?  :)

        1. Jul 24, 2011

          Exactly. So we do not allow that

  4. Jul 21, 2011

    Your pattern matching is nice, but I find the decomposition section hard to follow.  The feature appears baroque (ML envy, i.e., http://news.ycombinator.com/item?id=1412415 ?) or perhaps baroquely implemented.  Improved documentation and examples will help, but I'd prefer the syntax to be cleaner especially around decomposer functions.

    1. Jul 22, 2011

      Thanks for your comment, we'll see what we can do here.

  5. Jul 29, 2011

    Anonymous

    Looks like occurrence typing in Typed Racket.

  6. Aug 05, 2011

    Anonymous

    I suppose the patterns are tested from top to bottom? I hope you thought of guards (can't find nothing about those above).

    1. Aug 05, 2011

      Top to bottom, right. Guards can be done with continue, see this page.

  7. Jan 07, 2012

    Your decomposer functions are very much alike F#'s  active patterns, but seem less powerful. Have you considered adopting Active Patterns?

    1. Jan 07, 2012

      Could you expand on how decomposers are less powerful than Active Patterns? Thanks

      1. Jan 07, 2012

        Well, less powerfull was incorrect. But am I right, that one should define a decomposer function should be a member of the class, an instance of which is being matched? 

        Active patterns can be declared where you need them, for library types also 

        1. Jan 07, 2012

          A decomposer can be (and usually is) an extension function, i.e. it is defined outside the class it is decomposing.

          1. Jan 07, 2012

            Thank you. I got confused by the mmddyy() example

  8. Jan 07, 2012

    The binding patterns syntax is unintuitive as opposed to simpler patterns. I have to change the code significantly and It looks very different I switch to binding patterns

    if (x is #(Int, String,)) { 
    print("First component is Int") 
    print("Second component is Int") 
    }
    if (x is #(val a is Int,val b String,)) { 
    print("First component $a is Int") 
    print("Second component $b is Int") 
    }

    Why not

    if (x is #(Int a, String b)) { 
    print("First component $a is Int") 
    print("Second component $b is Int") 
    }

    The syntax for range pattern will be

    if (x is #(Int a in 1..20, String b))...
    1. Jan 07, 2012

      I guess, if you look at where the types are in Kotlin, it will be easy to see why not )

      1. Jan 07, 2012

        Sorry, but the some more questions still hold:

        * Why do I have to add the val keyword?

        * Can I specify the desired type in range pattern?
        *Will

        val myTuple : #(value : Double, Info : String) = #(1.2, "Some")

        match

        if (x is #(val a in 1..20, String b))...
        1. Jan 07, 2012

          Why do I have to add the val keyword?

          Because

          • you declare a new variable there, so we prefer to make this explicit (unlike other languages, yes) and prevent mistaking such declarations for expression occurrences
          • having no dependency on character case (unlike Haskell and ML) we prefer to syntactically distinguish between a type pattern and a binding pattern

          Can I specify the desired type in range pattern?

          Not explicitly. The desired type must be the type accepted by contains() method defined on the range. Note that it can be anything:

          if (x is #(val a in MyRange(1, "foo"), val b : String)) ...

          Your third question

          It won't compile: you have to create a range of doubles:

          if (x is #(val a in 1.0..20, val b : String))...
  9. Jun 02, 2012

    Hello. So pattern matching is not working yet, right? I've tried this thing, it seems it cannot equal objects:

    1. Jun 02, 2012

      This will work if you implement the equals() method on Toast. At the moment Kotlin does not provide any auto-generated equals() implementations.