Alias redefinition

HI,
In a shard i am developping, I have to use an alias for a union of types that will only be really defined when it is used in the final application

Example code :

Shard panel:

module Panel
...
include Enumerable(UserType)
...
end

User Application:

alias UserType = String | Int32 | MyType1 | MyType2 ...
require "panel"
...

By declaring the alias before the require in the app source code, the compilation proceeds normally and the application works correctly.

However, there are some drawbacks in the shard development process, for example:.

  • In the editor (vim), the lack of UserType definition generates alerts (not blocking, but annoying)
  • crystal doc does not generate the documentation, because of the missing UserType definition.

Is there an approach to this type of problem? A redefinition of alias in the user code ?

Thanks

Would it make sense for Panel to be a generic type so you can inject the Enumerable type?

module Panel(T)
  include Enumerable(T)
end

And then, assuming Panel is a mixin:

class MyClass
  include Panel(UserType)
end
1 Like

Unfortunately, module Panel is not meant to be used as a mixin, but as an external library.

Now, on reflection, this chicken-and-egg problem could simply be solved if the UserType type could be deduced from the generic Enumerable(T) type.

For example, given the following class:

class Test(T)
  def initialize(@data : Enumerable(T))
    p T
  end
end

t  = Test.new({'a' => [1, "A", 3.14]})

the output is:
Tuple(Char, Array(Float64 | Int32 | String))

How could I infer UserType from T so that it equals the union of Char | Float64 | Int32 | String ?

Would something like this work?

class Test(T)
  def initialize(@data : Enumerable({Char, Array(T)}))
    {% if T < Union(Char | Float64 | Int32 | String) %}
      p T
    {% else %}
      {% raise "Test type must be Char, Float64, Int32, or String. Got: #{T}" %}
    {% end %}
  end
end

t = Test.new({'a' => [1, "A", 3.14, "hi"]})

What is T and what is UserType? Your examples each ever contain only one of them. So it’s unclear what the connection would be.

Hmm, not easy to explain in a simple and concise way

Say I have a grid, with n columns and m lines, which I want to fill with data from an Enumerable (whatever contents).
Of course, each entry in the Enumerable will correspond to a line in the grid, but I now need to define which data should appear in which column.
To do this, I put the definition of each column in an array, including at least a label and a Proc to extract the data.
I then come to the following definitions in (pseudo)code:

class Column(T)
  def initialize(@label : String, @extract : T -> ???)
  end
end
 
class Line(T)
  include Enumerable(???)
  def initialize(@grid : Grid(T), @source : T)
  end
  def each
    yield ...
  end   
end
 
class Grid(T)
  include Enumerable(Line(T))
  def initialize(@sources : Enumerable(T))
    @columns = {} of String => Column(T)
  end
  def each
    yield ...
  end  
  def add_column(label, &extractor)
  	@columns[label] = extractor
  end
end

Say I have the following Enumerable as sources : data = {'a' => [1, "A", 3.14], 'b' => [2, "Z", 2.718]}
So I define the grid with :

g = Grid.new(data)
g.add_column("col 1") {|k, v| k}
g.add_column("col 2") {|k, v| v[2]}

and printing the grid would give :

a 3.14
b 2.718

In the code above, I have represented the type of data I am looking for with ???
In this example, the type ??? is Char | Float64 | Int32 | String while T is Tuple(Char, Array(Float64 | Int32 | String))

but with for example, a source data of [['a', 3.14], ['b', 2.718]], it would have been only Char | Float64

So, my question is, in this case, is it possible to derive an union of simple types from any type of Enumerable(T) to avoid to have to define manually an alias in the application source code with the drawbacks I mentioned in my first post?

Hope it’s more clear ?

Thanks

It seems that you are describing a solution. It would be better to instead describe the problem. Why you need to pass the data and columns information separately? And, ultimately what is the grid going to be used for? What is it modeling?

1 Like

Maybe the most important question is: what program are you building?

Well, it looks complicated apparently.
So I think I’ll stick with the version that works!

1 Like