# Casting problems with large number of types

**URL:** <https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181>\
**Category:** Help & Support\
**Created:** [May 27, 2020, 7:58pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181 "2020-05-27T19:58:15Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 27, 2020, 7:58pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/1 "2020-05-27T19:58:15Z")

</div>

If I don’t cast type type is `Fuse::LowLevel::Request, UInt64, (Fuse::Lib::FuseFileInfo | Int32 | LibC::Stat | Slice(UInt8) | String | UInt64 | Nil)`, which can’t be passed to the target method because of type restrictions.

If I cast I get the error at the bottom.

Code:

```auto
case mdata
  # Shouldn't this make mdata available as `{UInt64, String}`?
  when {UInt64, String}
    case name
      when :lookup
        lookup_data = mdata.as({UInt64, String})

```

Error:

```auto
can't cast (
Tuple(UInt64, Fuse::Lib::FuseFileInfo | Int32 | String | UInt64 | Nil) | 
Tuple(UInt64, LibC::Stat | Slice(UInt8) | String | UInt64, Int32 | Int64 | UInt16 | UInt64, Fuse::Lib::FuseFileInfo | String) | 
Tuple(UInt64, String | UInt64, String | UInt16))
to Tuple(UInt64, String)

```

Code: [https://github.com/didactic-drunk/fuse/tree/low\_level](https://github.com/didactic-drunk/fuse/tree/low_level)  
See `src/fuse/low_level.cr` `callback_receive_loop`  
Run using `./test`.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [May 27, 2020, 8:26pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/2 "2020-05-27T20:26:58Z")

</div>

You need to do `when Tuple(UInt64, String)`. In your code you are matching against a tuple value, not a tuple type.

If you want to match against a tuple type you need to use a tuple in the `case`, as explained in [https://crystal-lang.org/reference/syntax\_and\_semantics/case.html](https://crystal-lang.org/reference/syntax_and_semantics/case.html) (section “Tuple Literal”)

Edit: actually, the “Tuple Literal” stuff is not relevant. `{Foo, Bar}` is only a tuple type in the type grammar, not in regular code.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 27, 2020, 8:42pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/3 "2020-05-27T20:42:24Z")

</div>

> If you want to match against a tuple type you need to use a tuple in the `case` , as explained in [case - Crystal](https://crystal-lang.org/reference/syntax_and_semantics/case.html) (section “Tuple Literal”)

I did follow the book. I don’t see a reference to `Tuple(types)`. Is the book wrong?

### Comparing against a type will perform an is\_a? check

```auto
case {value1, value2}
when {String, Int32}
  # Matches if value1.is_a?(String) && value2.is_a?(Int32)
  # The type of value1 is known to be a String by the compiler,
  # and the type of value2 is known to be an Int32
end

```

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 27, 2020, 8:45pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/4 "2020-05-27T20:45:20Z")

</div>

Using the @def.return\_type returns a `Tuple()` to match against. When I tried the `Tuple()` syntax (returned from `return_type`) earlier it matched nothing.

Matching {Int64, String} **did** match. At runtime it matches.

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [May 27, 2020, 8:56pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/5 "2020-05-27T20:56:21Z")

</div>

Sorry, what I said is wrong. But you need to use `Tuple(...)` to match against a tuple type. Using `{...}` matches against a tuple value.

> Matching {Int64, String} **did** match. At runtime it matches.

So the value you have is actually a tuple that has Int64 as a type in the first position, and String in the second one? Then using `as` won’t work. You would need to do `as(Tuple(Int64.class, String.class))`.

I think the confusion happens because I can’t see all of your code, just an incomplete snippet so I can’t try anything.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 27, 2020, 9:05pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/6 "2020-05-27T21:05:47Z")

</div>

> I can’t see all of your code, just an incomplete snippet so I can’t try anything.

> <https://github.com/didactic-drunk/fuse/blob/01bbc8182dad536237401e0c446bb732f42dfe60/src/fuse/low_level.cr#L359>

Link was provided above with a shell script and test case. It’s macro heavy and easier to see the output when the macro fails. Which is what I posted.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 27, 2020, 9:08pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/7 "2020-05-27T21:08:29Z")

</div>

> [@asterite](#):
>
> So the value you have is actually a tuple that has Int64 as a type in the first position, and String in the second one? Then using `as` won’t work. You would need to do `as(Tuple(Int64.class, String.class))` .

Shouldn’t `when` cast the type for me within scope of the `when` statement?

```auto
case mdata
  when {UInt64, String}
     # mdata is auto cast within this block  

```

Maybe the nested case statements are throwing the compiler off?

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [May 27, 2020, 9:51pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/8 "2020-05-27T21:51:23Z")

</div>

As I said before, if you use when {…} you are not matching against a type but against a value. The compiler only casts when X when X is a type, not a value.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 27, 2020, 11:38pm UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/9 "2020-05-27T23:38:01Z")

</div>

> [@asterite](#):
>
> `Tuple(...)`

Matching using `when Tuple(Int32, Int32)` doesn’t work.

[https://carc.in/#/r/95ov](https://carc.in/#/r/95ov)

---

<div class="post-metadata">

**Author:** ![asterite](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/asterite/32/60_2.png) [@asterite](https://forum.crystal-lang.org/u/asterite)\
**Post date:** [May 28, 2020, 12:09am UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/10 "2020-05-28T00:09:45Z")

</div>

Thank you for the code snippet! With it I can finally understand the problem.

I think this is undocumented, but when you mix two tuple types of the same length, for example `Tuple(A, B) | Tuple(C, D)`, the type is turned into `Tuple(A | C, B | D)`. That means that matching against `Tuple(A, B)` or matching against `Tuple(C, D)` **will never match**.

Instead, I recommend not using tuple types and using `record` to represent the types that you want.

My advice is to avoid tuples and named tuples as much as possible.

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 28, 2020, 12:12am UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/11 "2020-05-28T00:12:56Z")

</div>

> [@asterite](#):
>
> You would need to do `as(Tuple(Int64.class, String.class))` .

Code:

```auto
case mdata
  when {UInt64, String}
    lookup_data = mdata.as(Tuple(UInt64.class, String.class))
# ^ as
    lookup(req, parent_ino: lookup_data[0], name: lookup_data[1])
end

def lookup(req : Request, parent_ino : UInt64, name : String) : Nil

```

Error:

```auto
Error: no overload matches 'Fuse::MidLevel::Handler#lookup' with types

 Fuse::LowLevel::Request, parent_ino: UInt64.class, name: String.class

```

---

<div class="post-metadata">

**Author:** ![Didactic.Drunk](https://avatars.discourse-cdn.com/v4/letter/d/eada6e/32.png) [@Didactic.Drunk](https://forum.crystal-lang.org/u/Didactic.Drunk)\
**Post date:** [May 28, 2020, 4:02am UTC](https://forum.crystal-lang.org/t/casting-problems-with-large-number-of-types/2181/12 "2020-05-28T04:02:29Z")

</div>

I’ll give record/struct a try.

My other solution would make a channel with fibers for each data type. Most apps would need `nproc `to `max_net_connecions` Fibers. I don’t know if `8-20 * ncpu` fibers with dedicated type channels is faster or a single pool of `ncpu` fibers inside of a giant `case statement`.
