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. |

24 Comments
comments.show.hideJul 20, 2011
Alexander Kosenkov
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()
Jul 20, 2011
Andrey Breslav
It is an extension function Any?.Tree()
Jul 21, 2011
Anonymous
Can regular functions (not declared as "decomposer") be used with "@"? What about free-form values of function types?
Jul 21, 2011
Andrey Breslav
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
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?
Jul 21, 2011
Andrey Breslav
You can say
if(a!=y&&aisFoo@(valb)){b.foo()// b is visible here}But not much more than that, because the scopes become too difficult to track for a human being.
Jul 23, 2011
B. K. Oxley (binkley)
"Too difficult to track for a human being". Code smell? :)
Jul 24, 2011
Andrey Breslav
Exactly. So we do not allow that
Jul 21, 2011
B. K. Oxley (binkley)
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.
Jul 22, 2011
Andrey Breslav
Thanks for your comment, we'll see what we can do here.
Jul 29, 2011
Anonymous
Looks like occurrence typing in Typed Racket.
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).
Aug 05, 2011
Andrey Breslav
Top to bottom, right. Guards can be done with continue, see this page.
Jan 07, 2012
Vasiliy Kudriavtsev
Your decomposer functions are very much alike F#'s active patterns, but seem less powerful. Have you considered adopting Active Patterns?
Jan 07, 2012
Andrey Breslav
Could you expand on how decomposers are less powerful than Active Patterns? Thanks
Jan 07, 2012
Vasiliy Kudriavtsev
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
Jan 07, 2012
Andrey Breslav
A decomposer can be (and usually is) an extension function, i.e. it is defined outside the class it is decomposing.
Jan 07, 2012
Vasiliy Kudriavtsev
Thank you. I got confused by the mmddyy() example
Jan 07, 2012
Vasiliy Kudriavtsev
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(xis#(Int,String,)){print("First component is Int")print("Second component is Int")}if(xis#(valaisInt,valbString,)){print("First component$ais Int")print("Second component$bis Int")}Why not
if(xis#(Inta,Stringb)){print("First component$ais Int")print("Second component$bis Int")}The syntax for range pattern will be
if(xis#(Intain1..20,Stringb))...Jan 07, 2012
Andrey Breslav
I guess, if you look at where the types are in Kotlin, it will be easy to see why not )
Jan 07, 2012
Vasiliy Kudriavtsev
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
valmyTuple:#(value:Double,Info:String)=#(1.2,"Some")match
if(xis#(valain1..20,Stringb))...Jan 07, 2012
Andrey Breslav
Because
Not explicitly. The desired type must be the type accepted by contains() method defined on the range. Note that it can be anything:
if(xis#(valainMyRange(1,"foo"),valb:String))...It won't compile: you have to create a range of doubles:
if(xis#(valain1.0..20,valb:String))...Jun 02, 2012
Vladimir Lichonos
Hello. So pattern matching is not working yet, right? I've tried this thing, it seems it cannot equal objects:
Jun 02, 2012
Dmitry Jemerov
This will work if you implement the equals() method on Toast. At the moment Kotlin does not provide any auto-generated equals() implementations.