Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cspell/pony-terms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ quiescence
quiescent
unmuted
unmutes
iftype
Zulip
15 changes: 15 additions & 0 deletions code-samples/control-structures-iftype-basic.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Animal
class Cat is Animal
class Dog is Animal

actor Main
new create(env: Env) =>
greet[Cat](env)
greet[Dog](env)

fun greet[A: Animal](env: Env) =>
iftype A <: Cat then
env.out.print("meow")
else
env.out.print("woof")
Comment thread
SeanTAllen marked this conversation as resolved.
Outdated
end
23 changes: 23 additions & 0 deletions code-samples/control-structures-iftype-capability.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
trait Animal

class Cat is Animal
var _name: String = "Cat"

fun box name(): String => _name
fun ref set_name(name': String) => _name = name'

actor Main
new create(env: Env) =>
let cat: Cat ref = Cat
maybe_rename[Cat ref](cat, env)

let cat2: Cat val = Cat
maybe_rename[Cat val](cat2, env)

fun maybe_rename[A: Animal](a: A, env: Env) =>
iftype A <: Cat ref then
a.set_name("Kitty")
env.out.print(a.name())
elseif A <: Cat box then
env.out.print(a.name())
end
19 changes: 19 additions & 0 deletions code-samples/control-structures-iftype-elseif.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
trait Animal
class Cat is Animal
class Dog is Animal
class Fish is Animal

actor Main
new create(env: Env) =>
describe[Cat](env)
describe[Dog](env)
describe[Fish](env)

fun describe[A: Animal](env: Env) =>
iftype A <: Cat then
env.out.print("I'm a cat")
elseif A <: Dog then
env.out.print("I'm a dog")
else
env.out.print("I'm something else")
end
20 changes: 20 additions & 0 deletions code-samples/control-structures-iftype-narrowing.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
trait Animal
fun name(): String

class Cat is Animal
fun name(): String => "Cat"
fun purr(): String => "prrr"

class Dog is Animal
fun name(): String => "Dog"

actor Main
new create(env: Env) =>
describe[Cat val](Cat, env)
describe[Dog val](Dog, env)

fun describe[A: Animal val](a: A, env: Env) =>
env.out.print(a.name())
iftype A <: Cat val then
env.out.print(a.purr())
end
15 changes: 15 additions & 0 deletions code-samples/control-structures-iftype-tuple.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Animal
class Cat is Animal
class Dog is Animal

actor Main
new create(env: Env) =>
check[Cat, Dog](env)
check[Cat, Cat](env)

fun check[A: Animal, B: Animal](env: Env) =>
iftype (A, B) <: (Cat, Dog) then
env.out.print("cat and dog")
else
env.out.print("some other combination")
end
8 changes: 4 additions & 4 deletions docs/appendices/keywords.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ This listing explains the usage of every Pony keyword.
| `consume` | move a value to a new variable, leaving the original variable empty |
| `digestof` | create a `USize` value that summarizes the Pony object, similar to a Java object's `hashCode()` value. |
| `do` | loop statement, or after a with statement |
| `else` | conditional statement in if, for, while, repeat, try (as a catch block), match |
| `elseif` | conditional statement, also used with `ifdef` |
| `else` | conditional statement in if, iftype, for, while, repeat, try (as a catch block), match |
| `elseif` | conditional statement, also used with `ifdef` and `iftype` |
| `embed` | embed a class as a field of another class |
| `end` | ending of: `if then`, `ifdef`, `while do`, `for in`, `repeat until`, `try`, `object`, `recover`, `match` |
| `end` | ending of: `if then`, `ifdef`, `iftype`, `while do`, `for in`, `repeat until`, `try`, `object`, `recover`, `match` |
| `error` | raises an error |
| `for` | loop statement |
| `fun` | define a function, executed synchronously |
Expand All @@ -45,7 +45,7 @@ This listing explains the usage of every Pony keyword.
| `repeat` | loop statement |
| `return` | to return early from a function |
| `tag` | reference capability – neither readable nor writeable, only object identity |
| `then` | (1) in if conditional statement |
| `then` | (1) in if and iftype conditional statements |
| | (2) as a (finally) block in try |
| `this` | the current object |
| `trait` | used in nominal subtyping: `class Foo is TraitName` |
Expand Down
5 changes: 3 additions & 2 deletions docs/appendices/symbol-lookup-cheat-sheet.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Pony, like just about any other programming language, has plenty of odd symbols
| `~` | Partial application |
| `?` | Partial function |
| `'` | Prime |
| `<:` | Subtype |
| `<:` | Subtype, iftype |

Here is a more elaborate explanation of Pony's use of special characters: (a line with (2) or (3) means an alternate usage of the symbol of the previous line)

Expand Down Expand Up @@ -67,4 +67,5 @@ Here is a more elaborate explanation of Pony's use of special characters: (a lin
| `->` | (1) arrow type |
| | (2) viewpoint |
| `._i` | where `i = 1,2,…` means the item at position i in the tuple |
| `<:` | "is a subtype of" or "can be substituted for" |
| `<:` | (1) "is a subtype of" or "can be substituted for" |
| | (2) used in `iftype` conditions to check subtype relationships |
68 changes: 68 additions & 0 deletions docs/expressions/control-structures.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,71 @@ Suppose we're trying to create something and we want to keep trying until it's g
Just like `while` loops, the value given by a `repeat` loop is the value of the expression within the loop on the last iteration, and `break` and `continue` can be used.

__Since you always go round a repeat loop at least once, do you ever need to give it an else expression?__ Yes, you may need to. A `continue` in the last iteration of a `repeat` loop needs to get a value from somewhere, and an `else` expression is used for that.

## Iftype

`iftype` is a compile-time conditional that selects a code path based on whether a type parameter satisfies a subtype constraint. It uses the `<:` operator to check the subtype relationship: `iftype A <: B` asks "is `A` a subtype of `B`?"

Because the condition is resolved at compile time, only the matching branch is included in the generated code. Both branches are type-checked, but the non-matching one is discarded from the output.

__How is iftype different from match?__ A `match` expression dispatches on the runtime type of a value. `iftype` operates on type parameters as they are known at compile time. If you call a generic function `foo[Animal](Cat)`, the type parameter is `Animal`, not `Cat` — so `iftype A <: Cat` would be false, even though the runtime value is a `Cat`.

Here's a simple example with a generic function that behaves differently depending on whether the type parameter is a specific class:

```pony
--8<-- "control-structures-iftype-basic.pony"
```

This prints "meow" and then "woof". When `greet` is called with `Cat`, the compiler sees that `Cat <: Cat` is true and takes the `then` branch. When called with `Dog`, `Dog <: Cat` is false, so it takes the `else` branch.
Comment thread
SeanTAllen marked this conversation as resolved.
Outdated

### Type narrowing

Inside the `then` branch, the compiler knows the subtype relationship holds. This means you can call methods on a value that are only available on the constraining type:

```pony
--8<-- "control-structures-iftype-narrowing.pony"
```

In the `then` branch the compiler knows `A <: Cat val`, so calling `a.purr()` is valid even though the method doesn't exist on the `Animal` trait.

### Elseif

You can chain multiple type checks with `elseif`, just like a regular `if`:

```pony
--8<-- "control-structures-iftype-elseif.pony"
```

### Capabilities in conditions

The condition can include a reference capability. This lets you write different code depending on what the caller can do with a value:

```pony
--8<-- "control-structures-iftype-capability.pony"
```

Here the `ref` branch can call `set_name` because it knows it has write access. The `box` branch can only read.

### Tuple conditions

When a function has multiple type parameters, you can check them together using a tuple condition. Both sides of `<:` must be tuples of the same size:

```pony
--8<-- "control-structures-iftype-tuple.pony"
```

The condition `(A, B) <: (Cat, Dog)` is true only when `A` is a subtype of `Cat` __and__ `B` is a subtype of `Dog`.

### Limitations

__Can I use iftype outside of a generic function?__ No. The subtype (the left side of `<:`) must be a type parameter or a tuple of type parameters. You cannot use concrete types. For example, this does not compile:

```pony
iftype String <: Stringable then
...
end
```

Like other control structures in Pony, `iftype` is an expression. Its value is the value of whichever branch is taken. If the `then` and `else` branches produce different types, the `iftype` expression produces a union of those types.

__What if my iftype doesn't have an else?__ Any `else` branch that doesn't exist gives an implicit `None`, just like `if`.
2 changes: 2 additions & 0 deletions docs/generics/generic-constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ In the previous section, we went through extra work to support `iso`. If there's
```pony
--8<-- "generic-constraints-foo-any-read.pony"
```

Constraints restrict what types a caller can provide. If you need different behavior depending on the actual type used, see [iftype](/expressions/control-structures.md#iftype) — a compile-time conditional that branches on subtype relationships.
Loading