# Defining types with tuples

**URL:** <https://forum.crystal-lang.org/t/defining-types-with-tuples/2696>\
**Category:** Help & Support\
**Created:** [November 22, 2020, 1:49am UTC](https://forum.crystal-lang.org/t/defining-types-with-tuples/2696 "2020-11-22T01:49:31Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![BrucePerens](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bruceperens/32/852_2.png) [@BrucePerens](https://forum.crystal-lang.org/u/BrucePerens)\
**Post date:** [November 22, 2020, 1:49am UTC](https://forum.crystal-lang.org/t/defining-types-with-tuples/2696/1 "2020-11-22T01:49:31Z")

</div>

I’m writing a wrapper for common generics, so that they can contain self-referential types without use of recursively-defined aliases. The code so far is below. I have two macros, `recursive_generic1` to make things like `Array(OneArgument)` and `recursive_generic2`, to make things like `Hash(Two, Arguments)`. I see that Iterable and Enumerable work with both Hash and Array, by using a tuple for the generic type argument. I don’t seem to be able to extract parts of a tuple to define types, though, code like this doesn’t work:

```crystal
tuple = {Symbol, String}
h = Hash(tuple[0], MyWrappingType)

```

Is there a way I can pick a field out of a tuple and use it as a type in a declaration?

Here is my code so far:

````auto
# Implementation of a pseudo-generic that contains itself without use of
# recursively-defined aliases, which are problematical and/or broken
# in the compiler.
#
# Bruce Perens (@BrucePerens, bruce@perens.com)
# MIT license. Copyright (C) 2000 Algorithmic LLC. In addition, this may
# be a derivative work of the works cited below:
#
# Thanks for lessons from:
# * Ary Borenszweig (@asterite) explained the way to implement this
# in https://github.com/crystal-lang/crystal/issues/5155
# * Sijawusz Pur Rahnama (@sija) and his `any_hash` shard.
# * Johannes Müller (@straight-shoota) and his `crinja` shard.
# * The Crystal stdlib implementation of the wrapped types, for the
# the API, methods, some of the tests.
# 
# name: The name of the new hash object.
# keytype: The type of keys in the hash.
# valuetype: The type of values in the hash.
#
# mutate_key: The name of a function that mutates the keys in the hash,
# and all values used to query keys in the hash.
# It takes the given key as
# its argument, and returns the mutated key. So, for example,
# this function would make all of the keys strings:
# ```crystal
# def mutate(key)
# key.to_s
# end
# ```
#
# mutate_value: The name of a function that mutates values as they are
# inserted in the hash. It takes the given value as
# its argument, and returns the mutated value. So, for example,
# this function would make all of the values strings as they
# are inserted in the hash:
# ```crystal
# def mutate(key)
# key.to_s
# end
# ```
macro recursive_generic1(name, generic, valuetype, mutate_key = nop, mutate_value = nop)
  class {{name.id}}
    {% verbatim do %}
      RETURN_SELF = %w(clear)
    {% end %}
    alias WrappedValue = GenericWrapper::ValueWrapper({{valuetype.id}})
    include GenericWrapper(({{valuetype}}),
     ::{{generic}}(WrappedValue),
     WrappedValue)

    module GenericWrapper::Mutate
      private def mutate_key(v)
        {{mutate_key}}(v)
      end

      private def mutate_value(v)
        {{mutate_value}}(v)
      end
    end
  end
end

macro recursive_generic2(name, generic, keytype, valuetype, mutate_key = nop, mutate_value = nop)
  class {{name.id}}
    {% verbatim do %}
      RETURN_SELF = %w(clear)
    {% end %}
    alias WrappedValue = GenericWrapper::ValueWrapper({{valuetype.id}})
    include GenericWrapper({({{keytype}}), ({{valuetype}})},
     ::{{generic}}({{keytype.id}}, WrappedValue),
     WrappedValue)

    module GenericWrapper::Mutate
      private def mutate_key(v)
        {{mutate_key}}(v)
      end

      private def mutate_value(v)
        {{mutate_value}}(v)
      end
    end
  end
end

module GenericWrapper(T, WrappedGeneric, WrappedValue)
  include Enumerable(T)
  include Iterable(T)

  macro method_missing(call)
    {% if RETURN_SELF.includes?((call).name.stringify) %}
      @contained.{{call}}
      self
    {% else %}
      @contained.{{call}}
    {% end %}
  end

  @contained = WrappedGeneric.new

  # Override the version in Enumerable, the one in the underlying generic
  # is faster.
  delegate size, to: @contained

  def []=(key, value)
    @contained[mutate_key(key)] = WrappedValue.new(mutate_value(value))
  end

  def [](key)
    v = @contained[mutate_key(key)]
  end

  def []?(key)
    @contained[mutate_key(key)]?
  end

  def each
    WrappedValue::Iterator.new(@contained.each)
  end

  def each(&block)
    i = @contained.each
    while (v = i.next).is_a?(Tuple)
      yield *extract_value(*v)
    end
  end

  def to_h
    self
  end

  private module ExtractValue
    def extract_value(key, value)
      { key, value.value }
    end
  end
  include ExtractValue

  struct ValueWrapper(ValueType)

    @value : ValueType

    def initialize(@value)
    end

    def value
      @value
    end

    def value=(n)
      @value = mutate_value(n)
    end

    class Iterator
      include ::Iterator(ValueWrapper)
      include ExtractValue
      @i : ::Iterator(ValueWrapper)

      def initialize(@i)
        super
      end

      def next
        v = @i.next
        if v == Iterator::Stop::INSTANCE
          stop
        else
          extract_value(*v)
        end
      end

      def rewind
        @i.rewind
        self
      end
    end
  end

  module Mutate
    # This No-Operation method is the default for mutate_value and mutate_key
    # It just returns its argument.
    private def nop(value)
      value
    end
  end
  include Mutate
end

recursive_generic2(MyHash, Hash, Symbol, String|MyHash|Array(MyHash))
recursive_generic1(MyArray, Array, String|MyHash|Array(MyHash))

h = MyHash.new
h[:itself] = h
h[:array] = [h]
p h.inspect
p h.size

````

---

<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:** [November 22, 2020, 12:23pm UTC](https://forum.crystal-lang.org/t/defining-types-with-tuples/2696/2 "2020-11-22T12:23:40Z")

</div>

You can use typeof

Sorry for the brevity but I can’t type more right now

---

<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:** [November 22, 2020, 9:15pm UTC](https://forum.crystal-lang.org/t/defining-types-with-tuples/2696/4 "2020-11-22T21:15:06Z")

</div>

What is your intention with the first line? Do you want a runtime tuple, or do you want to talk about a tuple type with those types? If it’s the latter case, you need to write that as Tuple(…).

---

<div class="post-metadata">

**Author:** ![BrucePerens](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bruceperens/32/852_2.png) [@BrucePerens](https://forum.crystal-lang.org/u/BrucePerens)\
**Post date:** [November 23, 2020, 12:24am UTC](https://forum.crystal-lang.org/t/defining-types-with-tuples/2696/6 "2020-11-23T00:24:45Z")

</div>

I think I have figured this out. I can do {{ include “#{pattern}”.id }} to include my custom generic with parameters.
