# Compile time / macro for generic type deduction?

**URL:** <https://forum.crystal-lang.org/t/compile-time-macro-for-generic-type-deduction/5911>\
**Category:** Help & Support\
**Created:** [August 5, 2023, 6:10pm UTC](https://forum.crystal-lang.org/t/compile-time-macro-for-generic-type-deduction/5911 "2023-08-05T18:10:26Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![wolfgang371](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wolfgang371/32/1665_2.png) [@wolfgang371](https://forum.crystal-lang.org/u/wolfgang371)\
**Post date:** [August 5, 2023, 6:10pm UTC](https://forum.crystal-lang.org/t/compile-time-macro-for-generic-type-deduction/5911/1 "2023-08-05T18:10:26Z")

</div>

This is related to [How to create a union type out of an array literal?](https://forum.crystal-lang.org/t/how-to-create-a-union-type-out-of-an-array-literal/5895/)

My original issue started out with a generic table that was supposed to get a (parametrized) reference type in addition. When I added the type, the compiler started complaining (`two` below), rightly so.

Since I was not able to deduce the underlying base type `BaseCell` inside `Table(T)` with the help of macros, I started passing both types to the generic class; which works, but somehow feels unwieldy.

In my non-reduced code I need even more generic types, so it gets kind of confusing.

I wonder if I miss some more elegant way…

```crystal
# e.g. `crystal run -Done reference.cr`

{% if flag?(:one) %}

# this works

class Table(T)
    @value : T|Nil = nil
    def m
        @value = ReferenceCell.new
    end
end

class ReferenceCell
end

alias BaseCell = Int32|Nil
alias Cell = BaseCell|ReferenceCell

t = Table(Cell).new
t.m

{% end %}

{% if flag?(:two) %}

# this doesn't work because of the wrong usage of type recursion

class Table(T)
    @value : T|Nil = nil
    def m
        @value = ReferenceCell(T).new # T is wrong here, would need BaseCell instead
# Error: instance variable '@value' of Table(Int32 | ReferenceCell(Int32 | Nil) | Nil)
# must be (Int32 | ReferenceCell(Int32 | Nil) | Nil)
# not ReferenceCell(Int32 | ReferenceCell(Int32 | Nil) | Nil)
    end
end

class ReferenceCell(T)
end

alias BaseCell = Int32|Nil
alias Cell = BaseCell|ReferenceCell(BaseCell)

t = Table(Cell).new
t.m

{% end %}

{% if flag?(:three) %}

# works again

class Table(T,U) # outward type, inner reference type
    @value : T|Nil = nil
    def m
        @value = ReferenceCell(U).new
    end
end

class ReferenceCell(T)
end

alias BaseCell = Int32|Nil
alias Cell = BaseCell|ReferenceCell(BaseCell)

t = Table(Cell, BaseCell).new
t.m

{% end %}

```

---

<div class="post-metadata">

**Author:** ![HertzDevil](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/hertzdevil/32/1023_2.png) [@HertzDevil](https://forum.crystal-lang.org/u/HertzDevil)\
**Post date:** [August 7, 2023, 6:54pm UTC](https://forum.crystal-lang.org/t/compile-time-macro-for-generic-type-deduction/5911/2 "2023-08-07T18:54:53Z")

</div>

Can `U` always be inferred from `T`? An example:

```crystal
class Table(T)
  @value : T?

  def m
    @value = ReferenceCell(typeof(begin
               x = uninitialized T
               x.is_a?(ReferenceCell) ? raise("") : x
             end)).new
  end
end

alias BaseCell = Int32 | Nil
alias Cell = BaseCell | ReferenceCell(BaseCell)

class ReferenceCell(T)
end

t = Table(Cell).new
typeof(t.m) # => ReferenceCell(Int32 | Nil)

```

---

<div class="post-metadata">

**Author:** ![wolfgang371](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wolfgang371/32/1665_2.png) [@wolfgang371](https://forum.crystal-lang.org/u/wolfgang371)\
**Post date:** [August 9, 2023, 2:52pm UTC](https://forum.crystal-lang.org/t/compile-time-macro-for-generic-type-deduction/5911/3 "2023-08-09T14:52:54Z")

</div>

Thanks, this works! 🙂  
But it cannot be put in an alias, so the code suffers anyhow. ☹
